To create a {gt} table, use gt() on a data frame.
library(gt)
library(dplyr)
gt(head(mtcars))
| mpg | cyl | disp | hp | drat | wt | qsec | vs | am | gear | carb |
|---|---|---|---|---|---|---|---|---|---|---|
| 21.0 | 6 | 160 | 110 | 3.90 | 2.620 | 16.46 | 0 | 1 | 4 | 4 |
| 21.0 | 6 | 160 | 110 | 3.90 | 2.875 | 17.02 | 0 | 1 | 4 | 4 |
| 22.8 | 4 | 108 | 93 | 3.85 | 2.320 | 18.61 | 1 | 1 | 4 | 1 |
| 21.4 | 6 | 258 | 110 | 3.08 | 3.215 | 19.44 | 1 | 0 | 3 | 1 |
| 18.7 | 8 | 360 | 175 | 3.15 | 3.440 | 17.02 | 0 | 0 | 3 | 2 |
| 18.1 | 6 | 225 | 105 | 2.76 | 3.460 | 20.22 | 1 | 0 | 3 | 1 |
# pipe also works just fine!
head(mtcars) %>%
gt()
| mpg | cyl | disp | hp | drat | wt | qsec | vs | am | gear | carb |
|---|---|---|---|---|---|---|---|---|---|---|
| 21.0 | 6 | 160 | 110 | 3.90 | 2.620 | 16.46 | 0 | 1 | 4 | 4 |
| 21.0 | 6 | 160 | 110 | 3.90 | 2.875 | 17.02 | 0 | 1 | 4 | 4 |
| 22.8 | 4 | 108 | 93 | 3.85 | 2.320 | 18.61 | 1 | 1 | 4 | 1 |
| 21.4 | 6 | 258 | 110 | 3.08 | 3.215 | 19.44 | 1 | 0 | 3 | 1 |
| 18.7 | 8 | 360 | 175 | 3.15 | 3.440 | 17.02 | 0 | 0 | 3 | 2 |
| 18.1 | 6 | 225 | 105 | 2.76 | 3.460 | 20.22 | 1 | 0 | 3 | 1 |
Sometimes you may want to see just a small portion of your input data. We can use gt_preview() in place of gt() to get the first x rows of data and the last y rows of data (which can be set by the top_n and bottom_n arguments).
gtcars %>%
dplyr::select(mfr, model, year) %>%
gt_preview()
| mfr | model | year | |
|---|---|---|---|
| 1 | Ford | GT | 2017 |
| 2 | Ferrari | 458 Speciale | 2015 |
| 3 | Ferrari | 458 Spider | 2015 |
| 4 | Ferrari | 458 Italia | 2014 |
| 5 | Ferrari | 488 GTB | 2016 |
| 6..46 | |||
| 47 | Rolls-Royce | Wraith | 2016 |
You can group rows in a table by specifying one or more columns in groupname_col:
head(mtcars) %>%
mutate(cyl = paste(cyl, "Cylinders")) %>%
gt(groupname_col = "cyl")
| mpg | disp | hp | drat | wt | qsec | vs | am | gear | carb |
|---|---|---|---|---|---|---|---|---|---|
| 6 Cylinders | |||||||||
| 21.0 | 160 | 110 | 3.90 | 2.620 | 16.46 | 0 | 1 | 4 | 4 |
| 21.0 | 160 | 110 | 3.90 | 2.875 | 17.02 | 0 | 1 | 4 | 4 |
| 21.4 | 258 | 110 | 3.08 | 3.215 | 19.44 | 1 | 0 | 3 | 1 |
| 18.1 | 225 | 105 | 2.76 | 3.460 | 20.22 | 1 | 0 | 3 | 1 |
| 4 Cylinders | |||||||||
| 22.8 | 108 | 93 | 3.85 | 2.320 | 18.61 | 1 | 1 | 4 | 1 |
| 8 Cylinders | |||||||||
| 18.7 | 360 | 175 | 3.15 | 3.440 | 17.02 | 0 | 0 | 3 | 2 |
Or by simply using dplyr::group_by()
head(mtcars) %>%
mutate(cyl = paste(cyl, "Cylinders")) %>%
group_by(cyl) %>%
gt()
| mpg | disp | hp | drat | wt | qsec | vs | am | gear | carb |
|---|---|---|---|---|---|---|---|---|---|
| 6 Cylinders | |||||||||
| 21.0 | 160 | 110 | 3.90 | 2.620 | 16.46 | 0 | 1 | 4 | 4 |
| 21.0 | 160 | 110 | 3.90 | 2.875 | 17.02 | 0 | 1 | 4 | 4 |
| 21.4 | 258 | 110 | 3.08 | 3.215 | 19.44 | 1 | 0 | 3 | 1 |
| 18.1 | 225 | 105 | 2.76 | 3.460 | 20.22 | 1 | 0 | 3 | 1 |
| 4 Cylinders | |||||||||
| 22.8 | 108 | 93 | 3.85 | 2.320 | 18.61 | 1 | 1 | 4 | 1 |
| 8 Cylinders | |||||||||
| 18.7 | 360 | 175 | 3.15 | 3.440 | 17.02 | 0 | 0 | 3 | 2 |
You can also create custom groups with gt::tab_row_group(). This is typically useful for creating your own groups within gt itself, and it can include specific rows based on a logical statement (ie hp > 600).
gtcars %>%
dplyr::select(model, year, hp, trq) %>%
head(8) %>%
gt() %>%
tab_row_group(
group = "powerful",
rows = hp <= 600
) %>%
tab_row_group(
group = "super powerful",
rows = hp > 600
)
| model | year | hp | trq |
|---|---|---|---|
| super powerful | |||
| GT | 2017 | 647 | 550 |
| 488 GTB | 2016 | 661 | 561 |
| GTC4Lusso | 2017 | 680 | 514 |
| FF | 2015 | 652 | 504 |
| powerful | |||
| 458 Speciale | 2015 | 597 | 398 |
| 458 Spider | 2015 | 562 | 398 |
| 458 Italia | 2014 | 562 | 398 |
| California | 2015 | 553 | 557 |
You can also create meta-groups of a grouping category this way.
gtcars %>%
dplyr::select(mfr:hp, mpg_c, mpg_h) %>%
dplyr::filter(mfr %in% c("Ford", "Dodge", "Chevrolet", "Nissan", "Acura")) %>%
gt() %>%
tab_row_group(
group = "Japanese",
rows = mfr %in% c("Nissan", "Acura")
) %>%
tab_row_group(
group = "American",
rows = mfr %in% c("Ford", "Dodge", "Chevrolet")
)
| mfr | model | year | trim | bdy_style | hp | mpg_c | mpg_h |
|---|---|---|---|---|---|---|---|
| American | |||||||
| Ford | GT | 2017 | Base Coupe | coupe | 647 | 11 | 18 |
| Chevrolet | Corvette | 2016 | Z06 Coupe | coupe | 650 | 15 | 22 |
| Dodge | Viper | 2017 | GT Coupe | coupe | 645 | 12 | 19 |
| Japanese | |||||||
| Acura | NSX | 2017 | Base Coupe | coupe | 573 | 21 | 22 |
| Nissan | GT-R | 2016 | Premium Coupe | coupe | 545 | 16 | 22 |
You can also convert a column into table rownames and specify it in the original gt() call.
head(mtcars) %>%
mutate(cyl = paste(cyl, "Cylinders")) %>%
gt(rowname_col = "cyl")
| mpg | disp | hp | drat | wt | qsec | vs | am | gear | carb | |
|---|---|---|---|---|---|---|---|---|---|---|
| 6 Cylinders | 21.0 | 160 | 110 | 3.90 | 2.620 | 16.46 | 0 | 1 | 4 | 4 |
| 6 Cylinders | 21.0 | 160 | 110 | 3.90 | 2.875 | 17.02 | 0 | 1 | 4 | 4 |
| 4 Cylinders | 22.8 | 108 | 93 | 3.85 | 2.320 | 18.61 | 1 | 1 | 4 | 1 |
| 6 Cylinders | 21.4 | 258 | 110 | 3.08 | 3.215 | 19.44 | 1 | 0 | 3 | 1 |
| 8 Cylinders | 18.7 | 360 | 175 | 3.15 | 3.440 | 17.02 | 0 | 0 | 3 | 2 |
| 6 Cylinders | 18.1 | 225 | 105 | 2.76 | 3.460 | 20.22 | 1 | 0 | 3 | 1 |
If you have a data.frame with rownames attached, you can use the rownames_to_stub argument to parse these properly.
head(mtcars) %>%
gt(rownames_to_stub = TRUE)
| mpg | cyl | disp | hp | drat | wt | qsec | vs | am | gear | carb | |
|---|---|---|---|---|---|---|---|---|---|---|---|
| Mazda RX4 | 21.0 | 6 | 160 | 110 | 3.90 | 2.620 | 16.46 | 0 | 1 | 4 | 4 |
| Mazda RX4 Wag | 21.0 | 6 | 160 | 110 | 3.90 | 2.875 | 17.02 | 0 | 1 | 4 | 4 |
| Datsun 710 | 22.8 | 4 | 108 | 93 | 3.85 | 2.320 | 18.61 | 1 | 1 | 4 | 1 |
| Hornet 4 Drive | 21.4 | 6 | 258 | 110 | 3.08 | 3.215 | 19.44 | 1 | 0 | 3 | 1 |
| Hornet Sportabout | 18.7 | 8 | 360 | 175 | 3.15 | 3.440 | 17.02 | 0 | 0 | 3 | 2 |
| Valiant | 18.1 | 6 | 225 | 105 | 2.76 | 3.460 | 20.22 | 1 | 0 | 3 | 1 |
However, a tibble will drop rownames, so you can convert a data.frame’s existing rownames to a column with tibble::rownames_to_column(). gt will automatically use columns named rowname as a rowname stub.
head(mtcars) %>%
tibble::rownames_to_column() %>%
gt()
| mpg | cyl | disp | hp | drat | wt | qsec | vs | am | gear | carb | |
|---|---|---|---|---|---|---|---|---|---|---|---|
| Mazda RX4 | 21.0 | 6 | 160 | 110 | 3.90 | 2.620 | 16.46 | 0 | 1 | 4 | 4 |
| Mazda RX4 Wag | 21.0 | 6 | 160 | 110 | 3.90 | 2.875 | 17.02 | 0 | 1 | 4 | 4 |
| Datsun 710 | 22.8 | 4 | 108 | 93 | 3.85 | 2.320 | 18.61 | 1 | 1 | 4 | 1 |
| Hornet 4 Drive | 21.4 | 6 | 258 | 110 | 3.08 | 3.215 | 19.44 | 1 | 0 | 3 | 1 |
| Hornet Sportabout | 18.7 | 8 | 360 | 175 | 3.15 | 3.440 | 17.02 | 0 | 0 | 3 | 2 |
| Valiant | 18.1 | 6 | 225 | 105 | 2.76 | 3.460 | 20.22 | 1 | 0 | 3 | 1 |
Combining rownames with groups can sometimes make the table easier to parse. Compare the two tables below:
mtcars %>%
head() %>%
select(cyl, mpg:drat) %>%
mutate(cyl = paste(cyl, "Cylinders")) %>%
gt(groupname_col = "cyl")
| mpg | disp | hp | drat |
|---|---|---|---|
| 6 Cylinders | |||
| 21.0 | 160 | 110 | 3.90 |
| 21.0 | 160 | 110 | 3.90 |
| 21.4 | 258 | 110 | 3.08 |
| 18.1 | 225 | 105 | 2.76 |
| 4 Cylinders | |||
| 22.8 | 108 | 93 | 3.85 |
| 8 Cylinders | |||
| 18.7 | 360 | 175 | 3.15 |
head(mtcars) %>%
select(cyl, mpg:drat) %>%
tibble::rownames_to_column() %>%
mutate(cyl = paste(cyl, "Cylinders")) %>%
gt(groupname_col = "cyl", rowname_col = "rowname")
| mpg | disp | hp | drat | |
|---|---|---|---|---|
| 6 Cylinders | ||||
| Mazda RX4 | 21.0 | 160 | 110 | 3.90 |
| Mazda RX4 Wag | 21.0 | 160 | 110 | 3.90 |
| Hornet 4 Drive | 21.4 | 258 | 110 | 3.08 |
| Valiant | 18.1 | 225 | 105 | 2.76 |
| 4 Cylinders | ||||
| Datsun 710 | 22.8 | 108 | 93 | 3.85 |
| 8 Cylinders | ||||
| Hornet Sportabout | 18.7 | 360 | 175 | 3.15 |
I typically will use a rowname column whenever I group data, but sometimes there may not be a “good” column to use here. You can pass in blank spaces to artificially move the group label to be presented closer to a “stub”.
head(mtcars) %>%
mutate(cyl = paste(cyl, "Cylinders")) %>%
mutate(blank_rowname = purrr::map(list(rep(" ", 8)), gt::html)) %>%
gt(rowname_col = "blank_rowname", groupname_col = "cyl")
| mpg | disp | hp | drat | wt | qsec | vs | am | gear | carb | |
|---|---|---|---|---|---|---|---|---|---|---|
| 6 Cylinders | ||||||||||
| 21.0 | 160 | 110 | 3.90 | 2.620 | 16.46 | 0 | 1 | 4 | 4 | |
| 21.0 | 160 | 110 | 3.90 | 2.875 | 17.02 | 0 | 1 | 4 | 4 | |
| 21.4 | 258 | 110 | 3.08 | 3.215 | 19.44 | 1 | 0 | 3 | 1 | |
| 18.1 | 225 | 105 | 2.76 | 3.460 | 20.22 | 1 | 0 | 3 | 1 | |
| 4 Cylinders | ||||||||||
| 22.8 | 108 | 93 | 3.85 | 2.320 | 18.61 | 1 | 1 | 4 | 1 | |
| 8 Cylinders | ||||||||||
| 18.7 | 360 | 175 | 3.15 | 3.440 | 17.02 | 0 | 0 | 3 | 2 | |
When rows are grouped, you can create summary rows in a column using the summary_rows function:
mtcars %>%
head(8) %>%
tibble::rownames_to_column(var = "name") %>%
mutate(cyl = paste(cyl, "Cylinders")) %>%
gt(groupname_col = "cyl", rowname_col = "name") %>%
summary_rows(
groups = TRUE,
fns = list(Average = ~mean(.))
)
| mpg | disp | hp | drat | wt | qsec | vs | am | gear | carb | |
|---|---|---|---|---|---|---|---|---|---|---|
| 6 Cylinders | ||||||||||
| Mazda RX4 | 21.0 | 160.0 | 110 | 3.90 | 2.620 | 16.46 | 0 | 1 | 4 | 4 |
| Mazda RX4 Wag | 21.0 | 160.0 | 110 | 3.90 | 2.875 | 17.02 | 0 | 1 | 4 | 4 |
| Hornet 4 Drive | 21.4 | 258.0 | 110 | 3.08 | 3.215 | 19.44 | 1 | 0 | 3 | 1 |
| Valiant | 18.1 | 225.0 | 105 | 2.76 | 3.460 | 20.22 | 1 | 0 | 3 | 1 |
| Average | 20.38 | 200.75 | 108.75 | 3.41 | 3.04 | 18.29 | 0.50 | 0.50 | 3.50 | 2.50 |
| 4 Cylinders | ||||||||||
| Datsun 710 | 22.8 | 108.0 | 93 | 3.85 | 2.320 | 18.61 | 1 | 1 | 4 | 1 |
| Merc 240D | 24.4 | 146.7 | 62 | 3.69 | 3.190 | 20.00 | 1 | 0 | 4 | 2 |
| Average | 23.60 | 127.35 | 77.50 | 3.77 | 2.75 | 19.30 | 1.00 | 0.50 | 4.00 | 1.50 |
| 8 Cylinders | ||||||||||
| Hornet Sportabout | 18.7 | 360.0 | 175 | 3.15 | 3.440 | 17.02 | 0 | 0 | 3 | 2 |
| Duster 360 | 14.3 | 360.0 | 245 | 3.21 | 3.570 | 15.84 | 0 | 0 | 3 | 4 |
| Average | 16.50 | 360.00 | 210.00 | 3.18 | 3.50 | 16.43 | 0.00 | 0.00 | 3.00 | 3.00 |
You can pass additional summarization functions to the fns argument, optionally specify columns to apply the summary to, and apply a formatter to format the output.
mtcars %>%
head(8) %>%
tibble::rownames_to_column(var = "name") %>%
mutate(cyl = paste(cyl, "Cylinders")) %>%
gt(groupname_col = "cyl", rowname_col = "name") %>%
summary_rows(
groups = TRUE,
columns = vars(mpg, disp, hp),
fns = list(
min = ~min(.),
max = ~max(.),
avg = ~mean(.)
),
formatter = fmt_number
)
| mpg | disp | hp | drat | wt | qsec | vs | am | gear | carb | |
|---|---|---|---|---|---|---|---|---|---|---|
| 6 Cylinders | ||||||||||
| Mazda RX4 | 21.0 | 160.0 | 110 | 3.90 | 2.620 | 16.46 | 0 | 1 | 4 | 4 |
| Mazda RX4 Wag | 21.0 | 160.0 | 110 | 3.90 | 2.875 | 17.02 | 0 | 1 | 4 | 4 |
| Hornet 4 Drive | 21.4 | 258.0 | 110 | 3.08 | 3.215 | 19.44 | 1 | 0 | 3 | 1 |
| Valiant | 18.1 | 225.0 | 105 | 2.76 | 3.460 | 20.22 | 1 | 0 | 3 | 1 |
| min | 18.10 | 160.00 | 105.00 | — | — | — | — | — | — | — |
| max | 21.40 | 258.00 | 110.00 | — | — | — | — | — | — | — |
| avg | 20.38 | 200.75 | 108.75 | — | — | — | — | — | — | — |
| 4 Cylinders | ||||||||||
| Datsun 710 | 22.8 | 108.0 | 93 | 3.85 | 2.320 | 18.61 | 1 | 1 | 4 | 1 |
| Merc 240D | 24.4 | 146.7 | 62 | 3.69 | 3.190 | 20.00 | 1 | 0 | 4 | 2 |
| min | 22.80 | 108.00 | 62.00 | — | — | — | — | — | — | — |
| max | 24.40 | 146.70 | 93.00 | — | — | — | — | — | — | — |
| avg | 23.60 | 127.35 | 77.50 | — | — | — | — | — | — | — |
| 8 Cylinders | ||||||||||
| Hornet Sportabout | 18.7 | 360.0 | 175 | 3.15 | 3.440 | 17.02 | 0 | 0 | 3 | 2 |
| Duster 360 | 14.3 | 360.0 | 245 | 3.21 | 3.570 | 15.84 | 0 | 0 | 3 | 4 |
| min | 14.30 | 360.00 | 175.00 | — | — | — | — | — | — | — |
| max | 18.70 | 360.00 | 245.00 | — | — | — | — | — | — | — |
| avg | 16.50 | 360.00 | 210.00 | — | — | — | — | — | — | — |
You can use any of R’s built-in aggregate functions, or a custom function.
# some examples
sum() # Sum of numbers
mean() # Mean of numbers
max() # Maximum of numbers
min() # Minimum of numbers
median() # Median of numbers
sd() # Standard Deviation of numbers
Or a custom aggregate function:
mode <- function(x) {
unique_var <- unique(x)
unique_var[which.max(tabulate(match(x, unique_var)))]
}
You can supply multiple groups via dplyr::group_by(), which are then appended with a - separator.
head(mtcars, 8) %>%
tibble::rownames_to_column(var = "name") %>%
mutate(cyl = paste(cyl, "Cylinders")) %>%
group_by(cyl, gear) %>%
arrange(cyl) %>%
gt(rowname_col = "name") %>%
summary_rows(
groups = TRUE,
fns = list(Average = ~mean(.))
)
| mpg | disp | hp | drat | wt | qsec | vs | am | carb | |
|---|---|---|---|---|---|---|---|---|---|
| 4 Cylinders - 4 | |||||||||
| Datsun 710 | 22.8 | 108.0 | 93 | 3.85 | 2.320 | 18.61 | 1 | 1 | 1 |
| Merc 240D | 24.4 | 146.7 | 62 | 3.69 | 3.190 | 20.00 | 1 | 0 | 2 |
| Average | 23.60 | 127.35 | 77.50 | 3.77 | 2.75 | 19.30 | 1.00 | 0.50 | 1.50 |
| 6 Cylinders - 4 | |||||||||
| Mazda RX4 | 21.0 | 160.0 | 110 | 3.90 | 2.620 | 16.46 | 0 | 1 | 4 |
| Mazda RX4 Wag | 21.0 | 160.0 | 110 | 3.90 | 2.875 | 17.02 | 0 | 1 | 4 |
| Average | 21.00 | 160.00 | 110.00 | 3.90 | 2.75 | 16.74 | 0.00 | 1.00 | 4.00 |
| 6 Cylinders - 3 | |||||||||
| Hornet 4 Drive | 21.4 | 258.0 | 110 | 3.08 | 3.215 | 19.44 | 1 | 0 | 1 |
| Valiant | 18.1 | 225.0 | 105 | 2.76 | 3.460 | 20.22 | 1 | 0 | 1 |
| Average | 19.75 | 241.50 | 107.50 | 2.92 | 3.34 | 19.83 | 1.00 | 0.00 | 1.00 |
| 8 Cylinders - 3 | |||||||||
| Hornet Sportabout | 18.7 | 360.0 | 175 | 3.15 | 3.440 | 17.02 | 0 | 0 | 2 |
| Duster 360 | 14.3 | 360.0 | 245 | 3.21 | 3.570 | 15.84 | 0 | 0 | 4 |
| Average | 16.50 | 360.00 | 210.00 | 3.18 | 3.50 | 16.43 | 0.00 | 0.00 | 3.00 |
Grand summary rows incorporate all of the available data, regardless of whether some of the data are part of row groups.
head(mtcars, 8) %>%
tibble::rownames_to_column(var = "name") %>%
mutate(cyl = paste(cyl, "Cylinders")) %>%
gt(rowname_col = "name", groupname_col = "cyl") %>%
grand_summary_rows(fns = list(Average = ~mean(.)))
| mpg | disp | hp | drat | wt | qsec | vs | am | gear | carb | |
|---|---|---|---|---|---|---|---|---|---|---|
| 6 Cylinders | ||||||||||
| Mazda RX4 | 21.0 | 160.0 | 110 | 3.90 | 2.620 | 16.46 | 0 | 1 | 4 | 4 |
| Mazda RX4 Wag | 21.0 | 160.0 | 110 | 3.90 | 2.875 | 17.02 | 0 | 1 | 4 | 4 |
| Hornet 4 Drive | 21.4 | 258.0 | 110 | 3.08 | 3.215 | 19.44 | 1 | 0 | 3 | 1 |
| Valiant | 18.1 | 225.0 | 105 | 2.76 | 3.460 | 20.22 | 1 | 0 | 3 | 1 |
| 4 Cylinders | ||||||||||
| Datsun 710 | 22.8 | 108.0 | 93 | 3.85 | 2.320 | 18.61 | 1 | 1 | 4 | 1 |
| Merc 240D | 24.4 | 146.7 | 62 | 3.69 | 3.190 | 20.00 | 1 | 0 | 4 | 2 |
| 8 Cylinders | ||||||||||
| Hornet Sportabout | 18.7 | 360.0 | 175 | 3.15 | 3.440 | 17.02 | 0 | 0 | 3 | 2 |
| Duster 360 | 14.3 | 360.0 | 245 | 3.21 | 3.570 | 15.84 | 0 | 0 | 3 | 4 |
| Average | 20.21 | 222.21 | 126.25 | 3.44 | 3.09 | 18.08 | 0.50 | 0.38 | 3.50 | 2.38 |
You can format data in a column by using the various fmt_??? functions:
info_date_style() # View a table with info on date styles
info_time_style() # View a table with info on time styles
info_currencies() # View a table with info on supported currencies
info_locales() # View a table with info on supported locales
exibbleThe exibble dataset is built into gt and has a lot of different formats to demo the specific fmt_??? functions.
dplyr::glimpse(exibble)
## Rows: 8
## Columns: 9
## $ num [3m[38;5;246m<dbl>[39m[23m 1.111e-01, 2.222e+00, 3.333e+01, 4.444e+02, 5.550e+03, NA, 7.770e+05, 8.880e+06
## $ char [3m[38;5;246m<chr>[39m[23m "apricot", "banana", "coconut", "durian", NA, "fig", "grapefruit", "honeydew"
## $ fctr [3m[38;5;246m<fct>[39m[23m one, two, three, four, five, six, seven, eight
## $ date [3m[38;5;246m<chr>[39m[23m "2015-01-15", "2015-02-15", "2015-03-15", "2015-04-15", "2015-05-15", "2015-06-15", NA, "2015-08-15"
## $ time [3m[38;5;246m<chr>[39m[23m "13:35", "14:40", "15:45", "16:50", "17:55", NA, "19:10", "20:20"
## $ datetime [3m[38;5;246m<chr>[39m[23m "2018-01-01 02:22", "2018-02-02 14:33", "2018-03-03 03:44", "2018-04-04 15:55", "2018-05-05 04:00", "20…
## $ currency [3m[38;5;246m<dbl>[39m[23m 49.950, 17.950, 1.390, 65100.000, 1325.810, 13.255, NA, 0.440
## $ row [3m[38;5;246m<chr>[39m[23m "row_1", "row_2", "row_3", "row_4", "row_5", "row_6", "row_7", "row_8"
## $ group [3m[38;5;246m<chr>[39m[23m "grp_a", "grp_a", "grp_a", "grp_a", "grp_b", "grp_b", "grp_b", "grp_b"
exibble %>%
gt(rowname_col = "row", groupname_col = "group") %>%
fmt_number(columns = vars(num)) %>%
fmt_date(columns = vars(date)) %>%
fmt_time(columns = vars(time)) %>%
fmt_datetime(columns = vars(datetime)) %>%
fmt_currency(columns = vars(currency))
| num | char | fctr | date | time | datetime | currency | |
|---|---|---|---|---|---|---|---|
| grp_a | |||||||
| row_1 | 0.11 | apricot | one | Thursday, January 15, 2015 | 13:35 | Monday, January 1, 2018 02:22 | $49.95 |
| row_2 | 2.22 | banana | two | Sunday, February 15, 2015 | 14:40 | Friday, February 2, 2018 14:33 | $17.95 |
| row_3 | 33.33 | coconut | three | Sunday, March 15, 2015 | 15:45 | Saturday, March 3, 2018 03:44 | $1.39 |
| row_4 | 444.40 | durian | four | Wednesday, April 15, 2015 | 16:50 | Wednesday, April 4, 2018 15:55 | $65,100.00 |
| grp_b | |||||||
| row_5 | 5,550.00 | NA | five | Friday, May 15, 2015 | 17:55 | Saturday, May 5, 2018 04:00 | $1,325.81 |
| row_6 | NA | fig | six | Monday, June 15, 2015 | NA | Wednesday, June 6, 2018 16:11 | $13.26 |
| row_7 | 777,000.00 | grapefruit | seven | NA | 19:10 | Saturday, July 7, 2018 05:22 | NA |
| row_8 | 8,880,000.00 | honeydew | eight | Saturday, August 15, 2015 | 20:20 | NA | $0.44 |
To use a specific locale for data formatting, provide specific arguments to the respective functions.
exibble %>%
select(date, time, datetime) %>%
gt(rowname_col = "row", groupname_col = "group") %>%
fmt_date(columns = vars(date), date_style = 3) %>%
fmt_time(columns = vars(time), time_style = 5) %>%
fmt_datetime(columns = vars(datetime), date_style = 6, time_style = 4)
| date | time | datetime |
|---|---|---|
| Thu, Jan 15, 2015 | 1 PM | Jan 1, 2018 2:22 AM |
| Sun, Feb 15, 2015 | 2 PM | Feb 2, 2018 2:33 PM |
| Sun, Mar 15, 2015 | 3 PM | Mar 3, 2018 3:44 AM |
| Wed, Apr 15, 2015 | 4 PM | Apr 4, 2018 3:55 PM |
| Fri, May 15, 2015 | 5 PM | May 5, 2018 4:00 AM |
| Mon, Jun 15, 2015 | NA | Jun 6, 2018 4:11 PM |
| NA | 7 PM | Jul 7, 2018 5:22 AM |
| Sat, Aug 15, 2015 | 8 PM | NA |
money <- data.frame(
USD = c(12.12, 2141.213, 0.42, 1.55, 34414),
EUR = c(10.68, 1884.27, 0.37, 1.36, 30284.32),
INR = c(861.07, 152122.48, 29.84, 110, 2444942.63),
JPY = c(1280, 226144, 44.36, 164, 3634634.61),
MAD = c(115.78, 20453.94, 4.01, 15, 328739.73)
)
money %>%
gt() %>%
fmt_currency(columns = vars(USD), currency = "USD") %>%
fmt_currency(columns = vars(EUR), currency = "EUR") %>%
fmt_currency(columns = vars(INR), currency = "INR") %>%
fmt_currency(columns = vars(JPY), currency = "JPY") %>%
fmt_currency(columns = vars(MAD), currency = "MAD")
| USD | EUR | INR | JPY | MAD |
|---|---|---|---|---|
| $12.12 | €10.68 | ₹861.07 | ¥1,280 | Dh115.78 |
| $2,141.21 | €1,884.27 | ₹152,122.48 | ¥226,144 | Dh20,453.94 |
| $0.42 | €0.37 | ₹29.84 | ¥44 | Dh4.01 |
| $1.55 | €1.36 | ₹110.00 | ¥164 | Dh15.00 |
| $34,414.00 | €30,284.32 | ₹2,444,942.63 | ¥3,634,635 | Dh328,739.73 |
data.frame(
x = 1:5,
y = 6:10,
percent = seq(from = 0.1, to = 0.2, by = 0.025)
) %>%
gt() %>%
fmt_percent(columns = vars(percent), decimals = 1)
| x | y | percent |
|---|---|---|
| 1 | 6 | 10.0% |
| 2 | 7 | 12.5% |
| 3 | 8 | 15.0% |
| 4 | 9 | 17.5% |
| 5 | 10 | 20.0% |
Numeric formatting can include changes to the number of decimals, separators (ie “,”), or even suffixing (ie K, Mb, etc).
exibble %>%
select(group, num, currency) %>%
gt() %>%
fmt_number(columns = vars(num), decimals = 4, sep_mark = "") %>%
# Suffixing scale and apply suffixes to larger numbers
fmt_number(columns = vars(currency), decimals = 1, suffixing = TRUE)
| group | num | currency |
|---|---|---|
| grp_a | 0.1111 | 50.0 |
| grp_a | 2.2220 | 17.9 |
| grp_a | 33.3300 | 1.4 |
| grp_a | 444.4000 | 65.1K |
| grp_b | 5550.0000 | 1.3K |
| grp_b | NA | 13.3 |
| grp_b | 777000.0000 | NA |
| grp_b | 8880000.0000 | 0.4 |
Missing values are ignored by formatters and shown as NA by default. You can specify missing values with other indicators with fmt_missing
exibble %>%
select(group, currency, num) %>%
gt() %>%
fmt_missing(columns = vars(currency), rows = is.na(currency)) %>%
fmt_missing(columns = vars(num), rows = is.na(num), missing_text = "none")
| group | currency | num |
|---|---|---|
| grp_a | 49.950 | 1.111e-01 |
| grp_a | 17.950 | 2.222e+00 |
| grp_a | 1.390 | 3.333e+01 |
| grp_a | 65100.000 | 4.444e+02 |
| grp_b | 1325.810 | 5.550e+03 |
| grp_b | 13.255 | none |
| grp_b | — | 7.770e+05 |
| grp_b | 0.440 | 8.880e+06 |
You can also parse cell content that contains arbitrary markdown.
# Create a few Markdown-based
# text snippets
text_1a <- "
### This is Markdown.
Markdown’s syntax is comprised entirely of
punctuation characters, which punctuation
characters have been carefully chosen so as
to look like what they mean... assuming
you’ve ever used email.
"
text_1b <- "
Info on Markdown syntax can be found
[here](https://daringfireball.net/projects/markdown/).
"
text_2a <- "
The **gt** package has these datasets:
- `countrypops`
- `sza`
- `gtcars`
- `sp500`
- `pizzaplace`
- `exibble`
"
text_2b <- "
There's a quick reference [here](https://commonmark.org/help/).
"
# Arrange the text snippets as a tibble
# using the `dplyr::tribble()` function;
# then, create a gt table and format
# all columns with `fmt_markdown()`
dplyr::tribble(
~Markdown, ~md,
text_1a, text_2a,
text_1b, text_2b,
) %>%
gt() %>%
fmt_markdown(columns = TRUE) %>%
tab_options(table.width = px(400))
| Markdown | md |
|---|---|
This is Markdown.Markdown’s syntax is comprised entirely of punctuation characters, which punctuation characters have been carefully chosen so as to look like what they mean... assuming you’ve ever used email. |
The gt package has these datasets:
|
Info on Markdown syntax can be found here. |
There's a quick reference here. |
If none of the built-in formatters apply to your data, you can use fmt() instead.
data.frame(
count = c(1L, 2L, 3L, 4L, 5L),
weight_g = c(150.65, 149.65, 171.28, 142.58, 139.04),
color = c("green", "yellow", "yellow", "green", "yellow")
) %>%
gt() %>%
fmt(
columns = vars(count),
fns = function(x){ paste(x, "bananas")}
)
| count | weight_g | color |
|---|---|---|
| 1 bananas | 150.65 | green |
| 2 bananas | 149.65 | yellow |
| 3 bananas | 171.28 | yellow |
| 4 bananas | 142.58 | green |
| 5 bananas | 139.04 | yellow |
data.frame(
count = c(1L, 2L, 3L, 4L, 5L),
weight_g = c(150.65, 149.65, 171.28, 142.58, 139.04),
color = c("green", "yellow", "yellow", "green", "yellow")
) %>%
gt() %>%
tab_header(
title = "Number of bananas, weight, and ripeness",
subtitle = "Bananas sourced in Mar 2021"
)
| Number of bananas, weight, and ripeness | ||
|---|---|---|
| Bananas sourced in Mar 2021 | ||
| count | weight_g | color |
| 1 | 150.65 | green |
| 2 | 149.65 | yellow |
| 3 | 171.28 | yellow |
| 4 | 142.58 | green |
| 5 | 139.04 | yellow |
You can parse markdown with md() or HTML with html().
data.frame(
count = c(1L, 2L, 3L, 4L, 5L),
weight_g = c(150.65, 149.65, 171.28, 142.58, 139.04),
color = c("green", "yellow", "yellow", "green", "yellow")
) %>%
gt() %>%
tab_header(
title = md("**Number of bananas, weight, and ripeness**"),
subtitle = html("Bananas sourced in <em><b>Mar 2021<b></em>")
)
| Number of bananas, weight, and ripeness | ||
|---|---|---|
| Bananas sourced in Mar 2021 | ||
| count | weight_g | color |
| 1 | 150.65 | green |
| 2 | 149.65 | yellow |
| 3 | 171.28 | yellow |
| 4 | 142.58 | green |
| 5 | 139.04 | yellow |
You can create column label “groups” with the tab_spanner() function.
head(gtcars, 8) %>%
dplyr::select(model:mpg_h, msrp) %>%
gt(rowname_col = "model") %>%
tab_spanner(
label = "Performance",
columns = vars(hp, hp_rpm, trq, trq_rpm, mpg_c, mpg_h)
) %>%
tab_spanner(
label = "Car Info",
columns = vars(year, bdy_style, trim)
)
| Car Info | Performance | msrp | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|
| year | bdy_style | trim | hp | hp_rpm | trq | trq_rpm | mpg_c | mpg_h | ||
| GT | 2017 | coupe | Base Coupe | 647 | 6250 | 550 | 5900 | 11 | 18 | 447000 |
| 458 Speciale | 2015 | coupe | Base Coupe | 597 | 9000 | 398 | 6000 | 13 | 17 | 291744 |
| 458 Spider | 2015 | convertible | Base | 562 | 9000 | 398 | 6000 | 13 | 17 | 263553 |
| 458 Italia | 2014 | coupe | Base Coupe | 562 | 9000 | 398 | 6000 | 13 | 17 | 233509 |
| 488 GTB | 2016 | coupe | Base Coupe | 661 | 8000 | 561 | 3000 | 15 | 22 | 245400 |
| California | 2015 | convertible | Base Convertible | 553 | 7500 | 557 | 4750 | 16 | 23 | 198973 |
| GTC4Lusso | 2017 | coupe | Base Coupe | 680 | 8250 | 514 | 5750 | 12 | 17 | 298000 |
| FF | 2015 | coupe | Base Coupe | 652 | 8000 | 504 | 6000 | 11 | 16 | 295000 |
For columns that are well formatted, gt can parse the delimiter and “split” the label into its component parts.
head(gtcars, 8) %>%
dplyr::select(model:trim, mpg_city = mpg_c, mpg_hwy = mpg_h) %>%
gt(rowname_col = "model") %>%
tab_spanner_delim(delim = "_")
| year | trim | mpg | ||
|---|---|---|---|---|
| city | hwy | |||
| GT | 2017 | Base Coupe | 11 | 18 |
| 458 Speciale | 2015 | Base Coupe | 13 | 17 |
| 458 Spider | 2015 | Base | 13 | 17 |
| 458 Italia | 2014 | Base Coupe | 13 | 17 |
| 488 GTB | 2016 | Base Coupe | 15 | 22 |
| California | 2015 | Base Convertible | 16 | 23 |
| GTC4Lusso | 2017 | Base Coupe | 12 | 17 |
| FF | 2015 | Base Coupe | 11 | 16 |
gt usesthe locations argument across many functions to let you tightly customize specific components. These are considered “Helper Functions”, and are further expended in the gt documentation.
Locations is used with the various cells_??? functions like: cells_title, cells_stubhead, cells_column_spanners().
For the cells_body(), it includes arguments for columns and rows, allowing you to specify specific columns or even columns + subsets of specific rows based on logicals.
head(gtcars, 8) %>%
dplyr::select(model:trim, mpg_city = mpg_c, mpg_hwy = mpg_h) %>%
gt(rowname_col = "model") %>%
tab_style(
style = cell_text(color = "red"),
locations = cells_body(
columns = vars(trim),
rows = trim == "Base Convertible"
)
)
| year | trim | mpg_city | mpg_hwy | |
|---|---|---|---|---|
| GT | 2017 | Base Coupe | 11 | 18 |
| 458 Speciale | 2015 | Base Coupe | 13 | 17 |
| 458 Spider | 2015 | Base | 13 | 17 |
| 458 Italia | 2014 | Base Coupe | 13 | 17 |
| 488 GTB | 2016 | Base Coupe | 15 | 22 |
| California | 2015 | Base Convertible | 16 | 23 |
| GTC4Lusso | 2017 | Base Coupe | 12 | 17 |
| FF | 2015 | Base Coupe | 11 | 16 |
Table header/title can be affected by cells_title, and it can affect either the title, subtitle or both (default).
head(gtcars, 8) %>%
dplyr::select(model:trim, mpg_city = mpg_c, mpg_hwy = mpg_h) %>%
gt() %>%
tab_header(
title = "These are not efficient cars",
subtitle = "But they are fast"
) %>%
tab_style(
style = cell_text(color = "black", weight = "bold", align = "left"),
locations = cells_title("title")
)
| These are not efficient cars | ||||
|---|---|---|---|---|
| But they are fast | ||||
| model | year | trim | mpg_city | mpg_hwy |
| GT | 2017 | Base Coupe | 11 | 18 |
| 458 Speciale | 2015 | Base Coupe | 13 | 17 |
| 458 Spider | 2015 | Base | 13 | 17 |
| 458 Italia | 2014 | Base Coupe | 13 | 17 |
| 488 GTB | 2016 | Base Coupe | 15 | 22 |
| California | 2015 | Base Convertible | 16 | 23 |
| GTC4Lusso | 2017 | Base Coupe | 12 | 17 |
| FF | 2015 | Base Coupe | 11 | 16 |
You can affect both the stubhead and the stub rows themselves as well.
head(gtcars, 8) %>%
dplyr::select(model:trim, mpg_city = mpg_c, mpg_hwy = mpg_h) %>%
gt(rowname_col = "model") %>%
tab_stubhead("Car Models") %>%
tab_style(
style = list(
cell_fill("black"),
cell_text(color = "white", weight = "bold")
),
locations = cells_stubhead()
) %>%
tab_style(
style = cell_text(color = "darkgrey", weight = "bold"),
locations = cells_stub()
)
| Car Models | year | trim | mpg_city | mpg_hwy |
|---|---|---|---|---|
| GT | 2017 | Base Coupe | 11 | 18 |
| 458 Speciale | 2015 | Base Coupe | 13 | 17 |
| 458 Spider | 2015 | Base | 13 | 17 |
| 458 Italia | 2014 | Base Coupe | 13 | 17 |
| 488 GTB | 2016 | Base Coupe | 15 | 22 |
| California | 2015 | Base Convertible | 16 | 23 |
| GTC4Lusso | 2017 | Base Coupe | 12 | 17 |
| FF | 2015 | Base Coupe | 11 | 16 |
Row groups can be further emphasized by changing the background or other styling.
gtcars %>%
filter(mfr %in% c("Ferrari", "Porsche")) %>%
dplyr::select(mfr, model:trim, mpg_city = mpg_c, mpg_hwy = mpg_h) %>%
gt(rowname_col = "model", groupname_col = "mfr") %>%
tab_style(
style = list(
cell_fill("black"),
cell_text(color = "white", weight = "bold")
),
locations = cells_row_groups()
) %>%
tab_style(
style = cell_text(color = "darkgrey", weight = "bold"),
locations = cells_stub()
)
| year | trim | mpg_city | mpg_hwy | |
|---|---|---|---|---|
| Ferrari | ||||
| 458 Speciale | 2015 | Base Coupe | 13 | 17 |
| 458 Spider | 2015 | Base | 13 | 17 |
| 458 Italia | 2014 | Base Coupe | 13 | 17 |
| 488 GTB | 2016 | Base Coupe | 15 | 22 |
| California | 2015 | Base Convertible | 16 | 23 |
| GTC4Lusso | 2017 | Base Coupe | 12 | 17 |
| FF | 2015 | Base Coupe | 11 | 16 |
| F12Berlinetta | 2015 | Base Coupe | 11 | 16 |
| LaFerrari | 2015 | Base Coupe | 12 | 16 |
| Porsche | ||||
| 718 Boxster | 2017 | Base Convertible | 21 | 28 |
| 718 Cayman | 2017 | Base Coupe | 20 | 29 |
| 911 | 2016 | Carrera Coupe | 20 | 28 |
| Panamera | 2016 | Base Sedan | 18 | 28 |
To affect the grouped summary rows (or grand summary rows) you can use cells_summary() or cells_grand_summary().
gtcars %>%
dplyr::filter(mfr %in% c("Ferrari", "Porsche", "Lamborghini")) %>%
dplyr::group_by(mfr) %>%
dplyr::slice_head(n = 3) %>%
dplyr::ungroup() %>%
dplyr::select(mfr, model:trim, mpg_city = mpg_c, mpg_hwy = mpg_h) %>%
gt(rowname_col = "model", groupname_col = "mfr") %>%
gt::summary_rows(
groups = TRUE, columns = vars(mpg_city, mpg_hwy),
fns = list(Average = ~mean(.)),
formatter = fmt_number, decimals = 1
) %>%
tab_style(
style = list(
cell_text(color = "white", font = google_font("Fira Mono")),
cell_fill("black")
),
locations = cells_summary()
)
| year | trim | mpg_city | mpg_hwy | |
|---|---|---|---|---|
| Ferrari | ||||
| 458 Speciale | 2015 | Base Coupe | 13 | 17 |
| 458 Spider | 2015 | Base | 13 | 17 |
| 458 Italia | 2014 | Base Coupe | 13 | 17 |
| Average | — | — | 13.0 | 17.0 |
| Lamborghini | ||||
| Aventador | 2015 | LP 700-4 Coupe | 11 | 18 |
| Huracan | 2015 | LP 610-4 Coupe | 16 | 20 |
| Gallardo | 2014 | LP 550-2 Coupe | 12 | 20 |
| Average | — | — | 13.0 | 19.3 |
| Porsche | ||||
| 718 Boxster | 2017 | Base Convertible | 21 | 28 |
| 718 Cayman | 2017 | Base Coupe | 20 | 29 |
| 911 | 2016 | Carrera Coupe | 20 | 28 |
| Average | — | — | 20.3 | 28.3 |
You can also affect the column labels or spanners above the labels. Note the use of a spanner id to make it easy to identify the specific spanner to apply the changes to.
exibble %>%
dplyr::select(-fctr, -currency, -group) %>%
gt(rowname_col = "row") %>%
tab_spanner(
label = "dates and times",
id = "dt",
columns = vars(date, time, datetime)
) %>%
tab_style(
style = cell_text(color = "darkgrey", transform = "uppercase"),
locations = cells_column_spanners(spanners = "dt")
) %>%
tab_style(
style = cell_text(weight = "bold"),
locations = cells_column_labels(columns = vars(date, time, datetime))
)
| num | char | dates and times | |||
|---|---|---|---|---|---|
| date | time | datetime | |||
| row_1 | 1.111e-01 | apricot | 2015-01-15 | 13:35 | 2018-01-01 02:22 |
| row_2 | 2.222e+00 | banana | 2015-02-15 | 14:40 | 2018-02-02 14:33 |
| row_3 | 3.333e+01 | coconut | 2015-03-15 | 15:45 | 2018-03-03 03:44 |
| row_4 | 4.444e+02 | durian | 2015-04-15 | 16:50 | 2018-04-04 15:55 |
| row_5 | 5.550e+03 | NA | 2015-05-15 | 17:55 | 2018-05-05 04:00 |
| row_6 | NA | fig | 2015-06-15 | NA | 2018-06-06 16:11 |
| row_7 | 7.770e+05 | grapefruit | NA | 19:10 | 2018-07-07 05:22 |
| row_8 | 8.880e+06 | honeydew | 2015-08-15 | 20:20 | NA |
You can also add footnotes or sourcenotes to arbitrary locations within the table. Both will “output” to the bottom of the table, but can place their respective indicators elsewhere.
Footnotes can be added to arbitrary locations with tab_footnote(). Here we add a footnote specifically to the mpg_h column label.
gtcars %>%
dplyr::select(model, year, trq, mpg_h) %>%
head(6) %>%
gt(rowname_col = "model") %>%
tab_footnote(
locations = cells_column_labels(vars(mpg_h)),
footnote = "Miles per Gallon on Highway"
)
| year | trq | mpg_h1 | |
|---|---|---|---|
| GT | 2017 | 550 | 18 |
| 458 Speciale | 2015 | 398 | 17 |
| 458 Spider | 2015 | 398 | 17 |
| 458 Italia | 2014 | 398 | 17 |
| 488 GTB | 2016 | 561 | 22 |
| California | 2015 | 557 | 23 |
|
1
Miles per Gallon on Highway
|
|||
The location argument allows for other areas to be specified, and the footnote argument can also parse markdown/HTML with the md() and html() helpers.
gtcars %>%
dplyr::select(model, year, trq, mpg_h) %>%
head(6) %>%
gt(rowname_col = "model") %>%
tab_footnote(
locations = cells_stub(rows = c(2,3,6)),
footnote = md("Manufacturing was interruped for these cars in **2015**")
)
| year | trq | mpg_h | |
|---|---|---|---|
| GT | 2017 | 550 | 18 |
| 458 Speciale1 | 2015 | 398 | 17 |
| 458 Spider1 | 2015 | 398 | 17 |
| 458 Italia | 2014 | 398 | 17 |
| 488 GTB | 2016 | 561 | 22 |
| California1 | 2015 | 557 | 23 |
|
1
Manufacturing was interruped for these cars in 2015
|
|||
Source notes, like “data sourced from…” can be added with tab_source_note(), and again it can parse arbitrary HTML or md.
source_tag <- "Data from <a href='https://www.edmunds.com'>Edmunds.com</a>"
gtcars %>%
dplyr::select(model, year, trq, mpg_h) %>%
head(6) %>%
gt(rowname_col = "model") %>%
tab_source_note(html(source_tag))
| year | trq | mpg_h | |
|---|---|---|---|
| GT | 2017 | 550 | 18 |
| 458 Speciale | 2015 | 398 | 17 |
| 458 Spider | 2015 | 398 | 17 |
| 458 Italia | 2014 | 398 | 17 |
| 488 GTB | 2016 | 561 | 22 |
| California | 2015 | 557 | 23 |
| Data from Edmunds.com | |||
You can transform specific portions of the table based on conditional logic.
Example conditionals include:
base:ifelse()
dplyr::if_else()
dplyr::case_when()
You can change specific text based on a function with text_transform(). This is extremely powerful, but specific to only the column being transformed.
data.frame(
count = c(1L, 2L, 3L, 4L, 5L),
weight_g = c(150.65, 149.65, 171.28, 142.58, 139.04),
color = c("green", "yellow", "yellow", "green", "yellow")
) %>%
gt() %>%
text_transform(
locations = cells_body(
columns = vars(weight_g)),
fn = function(x) {
paste0(
x, " (",
dplyr::case_when(
x > 150 ~ "large",
x <= 150 ~ "small"),
")")
}
)
| count | weight_g | color |
|---|---|---|
| 1 | 150.65 (large) | green |
| 2 | 149.65 (small) | yellow |
| 3 | 171.28 (large) | yellow |
| 4 | 142.58 (small) | green |
| 5 | 139.04 (small) | yellow |
You can logically match to rows and apply specific styling to them such as color.
stocks <- data.frame(
Symbol = c("GOOG", "FB", "AMZN", "NFLX", "TSLA"),
Price = c(1265.13, 187.89, 1761.33, 276.82, 328.13),
Change = c(4.14, 1.51, -19.45, 5.32, -12.45)
)
stocks %>%
gt() %>%
tab_style(
style = cell_text(color = "red", weight = "bold"),
locations = cells_body(
columns = vars(Change),
rows = Change < 0
)
) %>%
tab_style(
style = cell_text(color = "blue", weight = "bold"),
locations = cells_body(
columns = vars(Change),
rows = Change >= 0
)
)
| Symbol | Price | Change |
|---|---|---|
| GOOG | 1265.13 | 4.14 |
| FB | 187.89 | 1.51 |
| AMZN | 1761.33 | -19.45 |
| NFLX | 276.82 | 5.32 |
| TSLA | 328.13 | -12.45 |
With the tab_style() function we can target specific cells and apply styles to them. This is best done in conjunction with the helper functions cell_text(), cell_fill(), and cell_borders().
At present this function is focused on the application of styles for HTML output only (as such, other output formats will ignore all tab_style() calls). Using the aforementioned helper functions, here are some of the styles we can apply:
the background color of the cell (cell_fill(): color)
the cell’s text color, font, and size (cell_text(): color, font, size)
the text style (cell_text(): style), enabling the use of italics or oblique text.
the text weight (cell_text(): weight), allowing the use of thin to bold text (the degree of choice is greater with variable fonts)
the alignment and indentation of text (cell_text(): align and indent)
the cell borders (cell_borders())
data.frame(
count = c(1L, 2L, 3L, 4L, 5L),
weight_g = c(150.65, 149.65, 171.28, 142.58, 139.04),
color = c("green", "yellow", "yellow", "green", "yellow")
) %>%
gt() %>%
tab_style(
style = list(
cell_fill(color = "lightgrey"),
"font-variant: small-caps;"
),
locations = cells_body(columns = vars(color))
) %>%
tab_style(
style = list(
cell_text(color = "green")
),
locations = cells_body(
columns = vars(color),
# conditional logic
rows = color == "green"
)
) %>%
tab_style(
style = list(
cell_text(color = "goldenrod")
),
locations = cells_body(
columns = vars(color),
# conditional logic
rows = color == "yellow"
)
) %>%
tab_style(
style = list(
cell_borders(sides = "right", color = "black", weight = px(3))
),
locations = cells_body(
# entire column
columns = vars(weight_g)
)
) %>%
tab_style(
style = list(
cell_text(transform = "uppercase", weight = "bold")
),
# different location
locations = cells_column_labels(everything())
)
| count | weight_g | color |
|---|---|---|
| 1 | 150.65 | green |
| 2 | 149.65 | yellow |
| 3 | 171.28 | yellow |
| 4 | 142.58 | green |
| 5 | 139.04 | yellow |
The cols_label() function provides the flexibility to relabel one or more columns and we even have the option to use the md() or html() helper functions for rendering column labels from Markdown or using HTML.
head(mtcars) %>%
gt() %>%
cols_label(
mpg = "Miles/Gal",
cyl = "Cylinders"
)
| Miles/Gal | Cylinders | disp | hp | drat | wt | qsec | vs | am | gear | carb |
|---|---|---|---|---|---|---|---|---|---|---|
| 21.0 | 6 | 160 | 110 | 3.90 | 2.620 | 16.46 | 0 | 1 | 4 | 4 |
| 21.0 | 6 | 160 | 110 | 3.90 | 2.875 | 17.02 | 0 | 1 | 4 | 4 |
| 22.8 | 4 | 108 | 93 | 3.85 | 2.320 | 18.61 | 1 | 1 | 4 | 1 |
| 21.4 | 6 | 258 | 110 | 3.08 | 3.215 | 19.44 | 1 | 0 | 3 | 1 |
| 18.7 | 8 | 360 | 175 | 3.15 | 3.440 | 17.02 | 0 | 0 | 3 | 2 |
| 18.1 | 6 | 225 | 105 | 2.76 | 3.460 | 20.22 | 1 | 0 | 3 | 1 |
You can also parse markdown with md() or HTML with html() within the label string.
head(mtcars) %>%
gt() %>%
cols_label(
mpg = md("**Miles/Gal**"), # recognizes markdown syntax
cyl = html("<em>Cylinders</em>") # recognizes HTML syntax
)
| Miles/Gal | Cylinders | disp | hp | drat | wt | qsec | vs | am | gear | carb |
|---|---|---|---|---|---|---|---|---|---|---|
| 21.0 | 6 | 160 | 110 | 3.90 | 2.620 | 16.46 | 0 | 1 | 4 | 4 |
| 21.0 | 6 | 160 | 110 | 3.90 | 2.875 | 17.02 | 0 | 1 | 4 | 4 |
| 22.8 | 4 | 108 | 93 | 3.85 | 2.320 | 18.61 | 1 | 1 | 4 | 1 |
| 21.4 | 6 | 258 | 110 | 3.08 | 3.215 | 19.44 | 1 | 0 | 3 | 1 |
| 18.7 | 8 | 360 | 175 | 3.15 | 3.440 | 17.02 | 0 | 0 | 3 | 2 |
| 18.1 | 6 | 225 | 105 | 2.76 | 3.460 | 20.22 | 1 | 0 | 3 | 1 |
The individual alignments of columns (which includes the column labels and all of their data cells) can be modified. We have the option to align text to the left, the center, and the right.
For this example we’ve also included all the table lines to “show” the alignment a bit better.
countrypops %>%
dplyr::select(-contains("code")) %>%
dplyr::filter(country_name == "Mongolia") %>%
tail(5) %>%
gt() %>%
cols_align(
align = "left",
columns = vars(country_name)
) %>%
cols_align(
align = "center",
columns = vars(year)
) %>%
cols_align(
align = "right",
columns = vars(population)
) %>%
tab_options(table.width = px(300)) %>%
opt_table_lines()
| country_name | year | population |
|---|---|---|
| Mongolia | 2013 | 2869107 |
| Mongolia | 2014 | 2923896 |
| Mongolia | 2015 | 2976877 |
| Mongolia | 2016 | 3027398 |
| Mongolia | 2017 | 3075647 |
Typically, the best practice is to use left-align for text of variable length and right-align for numeric values. The reasoning can be highlighted in the table below. We want to align numeric values on the same scale so that they can be compared on the same scale, whereas text is more easily readable left-aligned. Center-align can be used with strings or values of equal-length.
Note that fmt_number and other fmt_??? applied to numeric will automatically right-align, but text will default to left-align.
gt::exibble %>%
select(group, char, num, currency) %>%
gt() %>%
cols_align(align = "center", columns = vars(group)) %>%
fmt_number(columns = vars(num)) %>%
fmt_currency(columns = vars(currency))
| group | char | num | currency |
|---|---|---|---|
| grp_a | apricot | 0.11 | $49.95 |
| grp_a | banana | 2.22 | $17.95 |
| grp_a | coconut | 33.33 | $1.39 |
| grp_a | durian | 444.40 | $65,100.00 |
| grp_b | NA | 5,550.00 | $1,325.81 |
| grp_b | fig | NA | $13.26 |
| grp_b | grapefruit | 777,000.00 | NA |
| grp_b | honeydew | 8,880,000.00 | $0.44 |
We choose which columns get specific widths. This can be in units of pixels (easily set by use of the
px()helper function), or, as percentages (where thepct()helper function is useful).
countrypops %>%
dplyr::select(-contains("code")) %>%
dplyr::filter(country_name == "Mongolia") %>%
tail(5) %>%
gt() %>%
cols_width(
vars(country_name) ~ px(200),
vars(year) ~ px(50),
vars(population) ~ px(100)
)
| country_name | year | population |
|---|---|---|
| Mongolia | 2013 | 2869107 |
| Mongolia | 2014 | 2923896 |
| Mongolia | 2015 | 2976877 |
| Mongolia | 2016 | 3027398 |
| Mongolia | 2017 | 3075647 |
You can use the everything() function to affect all columns (or remaining columns).
mtcars %>%
tibble::rownames_to_column("names") %>%
head(8) %>%
gt() %>%
cols_width(
vars(names) ~ px(150),
everything() ~ px(60)
) %>%
opt_table_lines()
| names | mpg | cyl | disp | hp | drat | wt | qsec | vs | am | gear | carb |
|---|---|---|---|---|---|---|---|---|---|---|---|
| Mazda RX4 | 21.0 | 6 | 160.0 | 110 | 3.90 | 2.620 | 16.46 | 0 | 1 | 4 | 4 |
| Mazda RX4 Wag | 21.0 | 6 | 160.0 | 110 | 3.90 | 2.875 | 17.02 | 0 | 1 | 4 | 4 |
| Datsun 710 | 22.8 | 4 | 108.0 | 93 | 3.85 | 2.320 | 18.61 | 1 | 1 | 4 | 1 |
| Hornet 4 Drive | 21.4 | 6 | 258.0 | 110 | 3.08 | 3.215 | 19.44 | 1 | 0 | 3 | 1 |
| Hornet Sportabout | 18.7 | 8 | 360.0 | 175 | 3.15 | 3.440 | 17.02 | 0 | 0 | 3 | 2 |
| Valiant | 18.1 | 6 | 225.0 | 105 | 2.76 | 3.460 | 20.22 | 1 | 0 | 3 | 1 |
| Duster 360 | 14.3 | 8 | 360.0 | 245 | 3.21 | 3.570 | 15.84 | 0 | 0 | 3 | 4 |
| Merc 240D | 24.4 | 4 | 146.7 | 62 | 3.69 | 3.190 | 20.00 | 1 | 0 | 4 | 2 |
You can move columns to the beginning, end, or arbitrary locations.
countrypops %>%
dplyr::select(country_name, year:population) %>%
tail(8) %>%
gt() %>%
cols_move_to_start(vars(year))
| year | country_name | population |
|---|---|---|
| 2010 | Zimbabwe | 14086317 |
| 2011 | Zimbabwe | 14386649 |
| 2012 | Zimbabwe | 14710826 |
| 2013 | Zimbabwe | 15054506 |
| 2014 | Zimbabwe | 15411675 |
| 2015 | Zimbabwe | 15777451 |
| 2016 | Zimbabwe | 16150362 |
| 2017 | Zimbabwe | 16529904 |
countrypops %>%
dplyr::select(country_name, year:population) %>%
tail(8) %>%
gt() %>%
cols_move_to_end(vars(year))
| country_name | population | year |
|---|---|---|
| Zimbabwe | 14086317 | 2010 |
| Zimbabwe | 14386649 | 2011 |
| Zimbabwe | 14710826 | 2012 |
| Zimbabwe | 15054506 | 2013 |
| Zimbabwe | 15411675 | 2014 |
| Zimbabwe | 15777451 | 2015 |
| Zimbabwe | 16150362 | 2016 |
| Zimbabwe | 16529904 | 2017 |
countrypops %>%
dplyr::select(country_name, year:population) %>%
tail(8) %>%
gt() %>%
cols_move(
columns = vars(country_name),
after = vars(year)
)
| year | country_name | population |
|---|---|---|
| 2010 | Zimbabwe | 14086317 |
| 2011 | Zimbabwe | 14386649 |
| 2012 | Zimbabwe | 14710826 |
| 2013 | Zimbabwe | 15054506 |
| 2014 | Zimbabwe | 15411675 |
| 2015 | Zimbabwe | 15777451 |
| 2016 | Zimbabwe | 16150362 |
| 2017 | Zimbabwe | 16529904 |
You can also hide arbitrary columns, but still reference them inside gt.
zim_code <- unique(countrypops$country_code_2) %>% .[length(.)]
countrypops %>%
tail(8) %>%
gt() %>%
cols_hide(columns = dplyr::contains("code")) %>%
tab_footnote(
footnote = paste("The country code is", zim_code),
locations = cells_body(
columns = vars(country_name),
rows = country_code_2 == zim_code
)
)
| country_name | year | population |
|---|---|---|
| Zimbabwe1 | 2010 | 14086317 |
| Zimbabwe1 | 2011 | 14386649 |
| Zimbabwe1 | 2012 | 14710826 |
| Zimbabwe1 | 2013 | 15054506 |
| Zimbabwe1 | 2014 | 15411675 |
| Zimbabwe1 | 2015 | 15777451 |
| Zimbabwe1 | 2016 | 16150362 |
| Zimbabwe1 | 2017 | 16529904 |
|
1
The country code is ZW
|
||
Columns can be merged with glue-like syntax.
sp500 %>%
dplyr::slice(50:55) %>%
dplyr::select(-volume, -adj_close) %>%
gt() %>%
cols_merge(
columns = vars(open, close),
hide_columns = vars(close),
pattern = "{1}—{2}"
) %>%
cols_merge(
columns = vars(low, high),
hide_columns = vars(high),
pattern = "{1}—{2}"
) %>%
cols_label(
open = "open/close",
low = "low/high"
)
| date | open/close | low/high |
|---|---|---|
| 2015-10-21 | 2033.47—2018.94 | 2017.22—2037.97 |
| 2015-10-20 | 2033.13—2030.77 | 2026.61—2039.12 |
| 2015-10-19 | 2031.73—2033.66 | 2022.31—2034.45 |
| 2015-10-16 | 2024.37—2033.11 | 2020.46—2033.54 |
| 2015-10-15 | 1996.47—2023.86 | 1996.47—2024.15 |
| 2015-10-14 | 2003.66—1994.24 | 1990.73—2009.56 |
You can customize table “theme” using several options, which can all be combined:
head(mtcars) %>%
gt() %>%
opt_table_lines("all")
| mpg | cyl | disp | hp | drat | wt | qsec | vs | am | gear | carb |
|---|---|---|---|---|---|---|---|---|---|---|
| 21.0 | 6 | 160 | 110 | 3.90 | 2.620 | 16.46 | 0 | 1 | 4 | 4 |
| 21.0 | 6 | 160 | 110 | 3.90 | 2.875 | 17.02 | 0 | 1 | 4 | 4 |
| 22.8 | 4 | 108 | 93 | 3.85 | 2.320 | 18.61 | 1 | 1 | 4 | 1 |
| 21.4 | 6 | 258 | 110 | 3.08 | 3.215 | 19.44 | 1 | 0 | 3 | 1 |
| 18.7 | 8 | 360 | 175 | 3.15 | 3.440 | 17.02 | 0 | 0 | 3 | 2 |
| 18.1 | 6 | 225 | 105 | 2.76 | 3.460 | 20.22 | 1 | 0 | 3 | 1 |
head(mtcars) %>%
gt() %>%
opt_table_lines("none")
| mpg | cyl | disp | hp | drat | wt | qsec | vs | am | gear | carb |
|---|---|---|---|---|---|---|---|---|---|---|
| 21.0 | 6 | 160 | 110 | 3.90 | 2.620 | 16.46 | 0 | 1 | 4 | 4 |
| 21.0 | 6 | 160 | 110 | 3.90 | 2.875 | 17.02 | 0 | 1 | 4 | 4 |
| 22.8 | 4 | 108 | 93 | 3.85 | 2.320 | 18.61 | 1 | 1 | 4 | 1 |
| 21.4 | 6 | 258 | 110 | 3.08 | 3.215 | 19.44 | 1 | 0 | 3 | 1 |
| 18.7 | 8 | 360 | 175 | 3.15 | 3.440 | 17.02 | 0 | 0 | 3 | 2 |
| 18.1 | 6 | 225 | 105 | 2.76 | 3.460 | 20.22 | 1 | 0 | 3 | 1 |
head(mtcars) %>%
gt() %>%
opt_table_outline()
| mpg | cyl | disp | hp | drat | wt | qsec | vs | am | gear | carb |
|---|---|---|---|---|---|---|---|---|---|---|
| 21.0 | 6 | 160 | 110 | 3.90 | 2.620 | 16.46 | 0 | 1 | 4 | 4 |
| 21.0 | 6 | 160 | 110 | 3.90 | 2.875 | 17.02 | 0 | 1 | 4 | 4 |
| 22.8 | 4 | 108 | 93 | 3.85 | 2.320 | 18.61 | 1 | 1 | 4 | 1 |
| 21.4 | 6 | 258 | 110 | 3.08 | 3.215 | 19.44 | 1 | 0 | 3 | 1 |
| 18.7 | 8 | 360 | 175 | 3.15 | 3.440 | 17.02 | 0 | 0 | 3 | 2 |
| 18.1 | 6 | 225 | 105 | 2.76 | 3.460 | 20.22 | 1 | 0 | 3 | 1 |
head(mtcars) %>%
gt() %>%
opt_row_striping()
| mpg | cyl | disp | hp | drat | wt | qsec | vs | am | gear | carb |
|---|---|---|---|---|---|---|---|---|---|---|
| 21.0 | 6 | 160 | 110 | 3.90 | 2.620 | 16.46 | 0 | 1 | 4 | 4 |
| 21.0 | 6 | 160 | 110 | 3.90 | 2.875 | 17.02 | 0 | 1 | 4 | 4 |
| 22.8 | 4 | 108 | 93 | 3.85 | 2.320 | 18.61 | 1 | 1 | 4 | 1 |
| 21.4 | 6 | 258 | 110 | 3.08 | 3.215 | 19.44 | 1 | 0 | 3 | 1 |
| 18.7 | 8 | 360 | 175 | 3.15 | 3.440 | 17.02 | 0 | 0 | 3 | 2 |
| 18.1 | 6 | 225 | 105 | 2.76 | 3.460 | 20.22 | 1 | 0 | 3 | 1 |
You can specify locations to add borders/dividers and control the weight/color/side of the border.
head(mtcars) %>%
gt() %>%
tab_style(
style = cell_borders(sides = "right", color = "black",
style = "dashed", weight = px(3)),
locations = cells_body(
columns = vars(cyl)
)
) %>%
tab_style(
style = cell_borders(sides = "bottom", color = "black", weight = px(3)),
locations = cells_column_labels(everything())
)
| mpg | cyl | disp | hp | drat | wt | qsec | vs | am | gear | carb |
|---|---|---|---|---|---|---|---|---|---|---|
| 21.0 | 6 | 160 | 110 | 3.90 | 2.620 | 16.46 | 0 | 1 | 4 | 4 |
| 21.0 | 6 | 160 | 110 | 3.90 | 2.875 | 17.02 | 0 | 1 | 4 | 4 |
| 22.8 | 4 | 108 | 93 | 3.85 | 2.320 | 18.61 | 1 | 1 | 4 | 1 |
| 21.4 | 6 | 258 | 110 | 3.08 | 3.215 | 19.44 | 1 | 0 | 3 | 1 |
| 18.7 | 8 | 360 | 175 | 3.15 | 3.440 | 17.02 | 0 | 0 | 3 | 2 |
| 18.1 | 6 | 225 | 105 | 2.76 | 3.460 | 20.22 | 1 | 0 | 3 | 1 |
You can also include locations outside of the cell body, ie the column labels or other locations.
head(mtcars) %>%
dplyr::select(cyl, everything()) %>%
gt() %>%
opt_table_lines("none") %>%
opt_row_striping() %>%
tab_style(
style = cell_borders(sides = "right", color = "black", weight = px(3)),
locations = cells_body(
columns = vars(cyl)
)
) %>%
tab_style(
style = cell_borders(sides = c("top", "bottom"),
color = "black", weight = px(3)),
locations = cells_column_labels(everything())
) %>%
tab_style(
style = cell_borders(sides = "bottom", color = "black", weight = px(3)),
locations = cells_body(rows = 6)
)
| cyl | mpg | disp | hp | drat | wt | qsec | vs | am | gear | carb |
|---|---|---|---|---|---|---|---|---|---|---|
| 6 | 21.0 | 160 | 110 | 3.90 | 2.620 | 16.46 | 0 | 1 | 4 | 4 |
| 6 | 21.0 | 160 | 110 | 3.90 | 2.875 | 17.02 | 0 | 1 | 4 | 4 |
| 4 | 22.8 | 108 | 93 | 3.85 | 2.320 | 18.61 | 1 | 1 | 4 | 1 |
| 6 | 21.4 | 258 | 110 | 3.08 | 3.215 | 19.44 | 1 | 0 | 3 | 1 |
| 8 | 18.7 | 360 | 175 | 3.15 | 3.440 | 17.02 | 0 | 0 | 3 | 2 |
| 6 | 18.1 | 225 | 105 | 2.76 | 3.460 | 20.22 | 1 | 0 | 3 | 1 |
You can use system fonts or bring in Google fonts with google_font().
# change font for entire table
head(mtcars) %>%
dplyr::select(cyl, everything()) %>%
gt() %>%
opt_table_font(font = google_font("Fira Mono"))
Adjusting font by location can be done via tab_style().
head(mtcars) %>%
dplyr::select(cyl, everything()) %>%
gt() %>%
# change cell body font
tab_style(
style = cell_text(
font = google_font("Fira Mono"), size = px(14)),
locations = cells_body(columns = everything())
) %>%
# change column labels
tab_style(
style = cell_text(
font = google_font("Indie Flower"),
weight = "bold",
size = px(30)
),
locations = cells_column_labels(everything())
)
| cyl | mpg | disp | hp | drat | wt | qsec | vs | am | gear | carb |
|---|---|---|---|---|---|---|---|---|---|---|
| 6 | 21.0 | 160 | 110 | 3.90 | 2.620 | 16.46 | 0 | 1 | 4 | 4 |
| 6 | 21.0 | 160 | 110 | 3.90 | 2.875 | 17.02 | 0 | 1 | 4 | 4 |
| 4 | 22.8 | 108 | 93 | 3.85 | 2.320 | 18.61 | 1 | 1 | 4 | 1 |
| 6 | 21.4 | 258 | 110 | 3.08 | 3.215 | 19.44 | 1 | 0 | 3 | 1 |
| 8 | 18.7 | 360 | 175 | 3.15 | 3.440 | 17.02 | 0 | 0 | 3 | 2 |
| 6 | 18.1 | 225 | 105 | 2.76 | 3.460 | 20.22 | 1 | 0 | 3 | 1 |
And the changes can be made outside of just the body of the table, for example the title/header.
countrypops %>%
dplyr::select(-contains("code")) %>%
tail(5) %>%
gt() %>%
tab_style(
style = cell_text(font = google_font("Fira Mono")),
locations = cells_body(columns = vars(year, population))
) %>%
tab_style(
style = cell_text(font = google_font("Raleway"), weight = "bold"),
locations = cells_body(columns = vars(country_name))
) %>%
tab_style(
style = cell_text(
font = google_font("Indie Flower"),
weight = "bold",
align = "left",
size = px(40)
),
locations = cells_title("title")
) %>%
tab_header("Population changes")
| Population changes | ||
|---|---|---|
| country_name | year | population |
| Zimbabwe | 2013 | 15054506 |
| Zimbabwe | 2014 | 15411675 |
| Zimbabwe | 2015 | 15777451 |
| Zimbabwe | 2016 | 16150362 |
| Zimbabwe | 2017 | 16529904 |
Modify the options available in a table. These options are named by the components, the subcomponents, and the element that can adjusted.
This is where the bulk of theme-changes can be done. tab_options has dozens of different table components that can be adjusted. The full details can be found in the {gt} documentation. You can customize all sorts of arbitrary components based globally.
head(mtcars) %>%
gt() %>%
tab_options(
table.background.color = "black",
column_labels.background.color = "grey",
column_labels.font.size = px(16),
table.font.size = px(12),
data_row.padding = px(4),
table.width = px(250)
)
| mpg | cyl | disp | hp | drat | wt | qsec | vs | am | gear | carb |
|---|---|---|---|---|---|---|---|---|---|---|
| 21.0 | 6 | 160 | 110 | 3.90 | 2.620 | 16.46 | 0 | 1 | 4 | 4 |
| 21.0 | 6 | 160 | 110 | 3.90 | 2.875 | 17.02 | 0 | 1 | 4 | 4 |
| 22.8 | 4 | 108 | 93 | 3.85 | 2.320 | 18.61 | 1 | 1 | 4 | 1 |
| 21.4 | 6 | 258 | 110 | 3.08 | 3.215 | 19.44 | 1 | 0 | 3 | 1 |
| 18.7 | 8 | 360 | 175 | 3.15 | 3.440 | 17.02 | 0 | 0 | 3 | 2 |
| 18.1 | 6 | 225 | 105 | 2.76 | 3.460 | 20.22 | 1 | 0 | 3 | 1 |
Creating a theme can be done by passing in a gt object, and setting some parameters in various gt functions. Here we define a basic theme.
my_gt_theme <- function(data, ...) {
data %>%
tab_options(
table.background.color = "black",
column_labels.background.color = "grey",
column_labels.font.size = px(16),
table.font.size = px(12),
data_row.padding = px(4),
...
)
}
And we can then apply that theme. Note that the theme is intentionally relative garish but we can see that we turned some of the arguments into a one-liner.
head(gtcars) %>%
gt() %>%
my_gt_theme(table.font.color.light = "lightgreen")
| mfr | model | year | trim | bdy_style | hp | hp_rpm | trq | trq_rpm | mpg_c | mpg_h | drivetrain | trsmn | ctry_origin | msrp |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Ford | GT | 2017 | Base Coupe | coupe | 647 | 6250 | 550 | 5900 | 11 | 18 | rwd | 7a | United States | 447000 |
| Ferrari | 458 Speciale | 2015 | Base Coupe | coupe | 597 | 9000 | 398 | 6000 | 13 | 17 | rwd | 7a | Italy | 291744 |
| Ferrari | 458 Spider | 2015 | Base | convertible | 562 | 9000 | 398 | 6000 | 13 | 17 | rwd | 7a | Italy | 263553 |
| Ferrari | 458 Italia | 2014 | Base Coupe | coupe | 562 | 9000 | 398 | 6000 | 13 | 17 | rwd | 7a | Italy | 233509 |
| Ferrari | 488 GTB | 2016 | Base Coupe | coupe | 661 | 8000 | 561 | 3000 | 15 | 22 | rwd | 7a | Italy | 245400 |
| Ferrari | California | 2015 | Base Convertible | convertible | 553 | 7500 | 557 | 4750 | 16 | 23 | rwd | 7a | Italy | 198973 |
A “prettier” theme based off an ESPN table style.
gt_theme_espn <- function(data, ...){
data %>%
opt_all_caps() %>%
opt_table_font(
font = list(
google_font("Lato"),
default_fonts()
)
) %>%
opt_row_striping() %>%
tab_options(
row.striping.background_color = "#fafafa",
table_body.hlines.color = "#f6f7f7",
source_notes.font.size = 12,
table.font.size = 16,
table.width = px(700),
heading.align = "left",
heading.title.font.size = 24,
table.border.top.color = "transparent",
table.border.top.width = px(3),
data_row.padding = px(7),
...
)
}
head(gtcars) %>%
dplyr::select(mfr:mpg_c) %>%
gt() %>%
gt_theme_espn()
| mfr | model | year | trim | bdy_style | hp | hp_rpm | trq | trq_rpm | mpg_c |
|---|---|---|---|---|---|---|---|---|---|
| Ford | GT | 2017 | Base Coupe | coupe | 647 | 6250 | 550 | 5900 | 11 |
| Ferrari | 458 Speciale | 2015 | Base Coupe | coupe | 597 | 9000 | 398 | 6000 | 13 |
| Ferrari | 458 Spider | 2015 | Base | convertible | 562 | 9000 | 398 | 6000 | 13 |
| Ferrari | 458 Italia | 2014 | Base Coupe | coupe | 562 | 9000 | 398 | 6000 | 13 |
| Ferrari | 488 GTB | 2016 | Base Coupe | coupe | 661 | 8000 | 561 | 3000 | 15 |
| Ferrari | California | 2015 | Base Convertible | convertible | 553 | 7500 | 557 | 4750 | 16 |
For more control over styling, you can add custom class names to the table and apply your own CSS. Note that this can require more effort than the built in gt functions, but also allows some things that aren’t possible by the functions align (like hover highlighting!).
exibble %>%
dplyr::select(num, currency) %>%
gt(id = "one") %>% # need to name the table so that you can apply CSS
fmt_currency(
columns = vars(currency),
currency = "HKD"
) %>%
fmt_scientific(
columns = vars(num)
) %>%
opt_css(
css = "
#one .gt_table {
background-color: lightgrey;
}
#one .gt_row {
padding: 20px 30px;
}
#one tr:hover {
background-color: #f5f8ff;
}
#one .gt_col_heading {
text-align: center !important;
}
"
)
| num | currency |
|---|---|
| 1.11 × 10−1 | HK$49.95 |
| 2.22 | HK$17.95 |
| 3.33 × 101 | HK$1.39 |
| 4.44 × 102 | HK$65,100.00 |
| 5.55 × 103 | HK$1,325.81 |
| NA | HK$13.26 |
| 7.77 × 105 | NA |
| 8.88 × 106 | HK$0.44 |
The examples here embed CSS for demonstration, but it’s often better to put CSS in an external style sheet. You can learn more about adding custom CSS to R Markdown documents here, or to Shiny apps here.
You can also use things like htmltools or glue to arbitrarily build HTML content like hyperlinks.
ex_sites <- data.frame(
Address = c("https://google.com", "https://yahoo.com", "https://duckduckgo.com"),
Site = c("Google", "Yahoo", "DuckDuckGo")
)
gt(ex_sites) %>%
text_transform(
locations = cells_body(columns = vars(Address)),
fn = function(x) {
purrr::map(x, ~htmltools::tags$a(href = .x, target = "_blank", .x))
}
) %>%
text_transform(
locations = cells_body(columns = vars(Site)),
fn = function(x) {
purrr::map2(
.x = x, .y = ex_sites$Address,
.f = ~glue::glue('<a href="{.y}" target="_blank">{.x}</a>'))
}
)
To add color scales, you can use R’s built-in color utilities (or other color manipulation packages like {paletteer}):
It’s possible to add color to data cells according to their values. The data_color() function colors all rows of any columns supplied. There are two ways to define how cells are colored: (1) through the use of a supplied color palette, and (2) through use of a color mapping function available from the {scales} package. The first method colorizes cell data according to whether values are character or numeric. The second method provides more control over how cells are colored since we provide an explicit color function and thus other requirements such as bin counts, cut points, or a numeric domain.
countrypops %>%
dplyr::filter(country_name == "Mongolia") %>%
dplyr::select(-contains("code")) %>%
tail(10) %>%
gt() %>%
data_color(
columns = vars(population),
colors = scales::col_numeric(
palette = c(
"white", "orange", "red"),
domain = NULL)
)
| country_name | year | population |
|---|---|---|
| Mongolia | 2008 | 2628131 |
| Mongolia | 2009 | 2668289 |
| Mongolia | 2010 | 2712650 |
| Mongolia | 2011 | 2761516 |
| Mongolia | 2012 | 2814226 |
| Mongolia | 2013 | 2869107 |
| Mongolia | 2014 | 2923896 |
| Mongolia | 2015 | 2976877 |
| Mongolia | 2016 | 3027398 |
| Mongolia | 2017 | 3075647 |
This can also be applied across multiple columns at once. Here’s an example using the built in nottem dataset. While red-green color scales are very commonly used, they are not color-blind friendly.
We can alternatively use something like red-white-blue, or purple-white-green.
dimnames <- list(start(nottem)[1]:end(nottem)[1], month.abb)
temps <- matrix(nottem, ncol = 12, byrow = TRUE, dimnames = dimnames) %>%
data.frame() %>%
tibble::rownames_to_column() %>%
head(10)
temps %>%
gt() %>%
data_color(
columns = vars(month.abb),
colors = scales::col_numeric(
c("#63be7b", "#ffeb84", "#f87274"),
domain = range(nottem))
)
| Jan | Feb | Mar | Apr | May | Jun | Jul | Aug | Sep | Oct | Nov | Dec | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 1920 | 40.6 | 40.8 | 44.4 | 46.7 | 54.1 | 58.5 | 57.7 | 56.4 | 54.3 | 50.5 | 42.9 | 39.8 |
| 1921 | 44.2 | 39.8 | 45.1 | 47.0 | 54.1 | 58.7 | 66.3 | 59.9 | 57.0 | 54.2 | 39.7 | 42.8 |
| 1922 | 37.5 | 38.7 | 39.5 | 42.1 | 55.7 | 57.8 | 56.8 | 54.3 | 54.3 | 47.1 | 41.8 | 41.7 |
| 1923 | 41.8 | 40.1 | 42.9 | 45.8 | 49.2 | 52.7 | 64.2 | 59.6 | 54.4 | 49.2 | 36.3 | 37.6 |
| 1924 | 39.3 | 37.5 | 38.3 | 45.5 | 53.2 | 57.7 | 60.8 | 58.2 | 56.4 | 49.8 | 44.4 | 43.6 |
| 1925 | 40.0 | 40.5 | 40.8 | 45.1 | 53.8 | 59.4 | 63.5 | 61.0 | 53.0 | 50.0 | 38.1 | 36.3 |
| 1926 | 39.2 | 43.4 | 43.4 | 48.9 | 50.6 | 56.8 | 62.5 | 62.0 | 57.5 | 46.7 | 41.6 | 39.8 |
| 1927 | 39.4 | 38.5 | 45.3 | 47.1 | 51.7 | 55.0 | 60.4 | 60.5 | 54.7 | 50.3 | 42.3 | 35.2 |
| 1928 | 40.8 | 41.1 | 42.8 | 47.3 | 50.9 | 56.4 | 62.2 | 60.5 | 55.4 | 50.2 | 43.0 | 37.3 |
| 1929 | 34.8 | 31.3 | 41.0 | 43.9 | 53.1 | 56.9 | 62.5 | 60.3 | 59.8 | 49.2 | 42.9 | 41.9 |
temps %>%
gt() %>%
data_color(
columns = vars(month.abb),
colors = scales::col_numeric(
colorspace::diverge_hcl(n = 9, palette = "Purple-Green") %>% rev(),
domain = range(nottem))
)
| Jan | Feb | Mar | Apr | May | Jun | Jul | Aug | Sep | Oct | Nov | Dec | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 1920 | 40.6 | 40.8 | 44.4 | 46.7 | 54.1 | 58.5 | 57.7 | 56.4 | 54.3 | 50.5 | 42.9 | 39.8 |
| 1921 | 44.2 | 39.8 | 45.1 | 47.0 | 54.1 | 58.7 | 66.3 | 59.9 | 57.0 | 54.2 | 39.7 | 42.8 |
| 1922 | 37.5 | 38.7 | 39.5 | 42.1 | 55.7 | 57.8 | 56.8 | 54.3 | 54.3 | 47.1 | 41.8 | 41.7 |
| 1923 | 41.8 | 40.1 | 42.9 | 45.8 | 49.2 | 52.7 | 64.2 | 59.6 | 54.4 | 49.2 | 36.3 | 37.6 |
| 1924 | 39.3 | 37.5 | 38.3 | 45.5 | 53.2 | 57.7 | 60.8 | 58.2 | 56.4 | 49.8 | 44.4 | 43.6 |
| 1925 | 40.0 | 40.5 | 40.8 | 45.1 | 53.8 | 59.4 | 63.5 | 61.0 | 53.0 | 50.0 | 38.1 | 36.3 |
| 1926 | 39.2 | 43.4 | 43.4 | 48.9 | 50.6 | 56.8 | 62.5 | 62.0 | 57.5 | 46.7 | 41.6 | 39.8 |
| 1927 | 39.4 | 38.5 | 45.3 | 47.1 | 51.7 | 55.0 | 60.4 | 60.5 | 54.7 | 50.3 | 42.3 | 35.2 |
| 1928 | 40.8 | 41.1 | 42.8 | 47.3 | 50.9 | 56.4 | 62.2 | 60.5 | 55.4 | 50.2 | 43.0 | 37.3 |
| 1929 | 34.8 | 31.3 | 41.0 | 43.9 | 53.1 | 56.9 | 62.5 | 60.3 | 59.8 | 49.2 | 42.9 | 41.9 |
temps %>%
gt() %>%
data_color(
columns = vars(month.abb),
colors = scales::col_numeric(
colorspace::diverge_hcl(n = 9, palette = "Blue-Red 3"),
domain = range(nottem))
)
| Jan | Feb | Mar | Apr | May | Jun | Jul | Aug | Sep | Oct | Nov | Dec | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 1920 | 40.6 | 40.8 | 44.4 | 46.7 | 54.1 | 58.5 | 57.7 | 56.4 | 54.3 | 50.5 | 42.9 | 39.8 |
| 1921 | 44.2 | 39.8 | 45.1 | 47.0 | 54.1 | 58.7 | 66.3 | 59.9 | 57.0 | 54.2 | 39.7 | 42.8 |
| 1922 | 37.5 | 38.7 | 39.5 | 42.1 | 55.7 | 57.8 | 56.8 | 54.3 | 54.3 | 47.1 | 41.8 | 41.7 |
| 1923 | 41.8 | 40.1 | 42.9 | 45.8 | 49.2 | 52.7 | 64.2 | 59.6 | 54.4 | 49.2 | 36.3 | 37.6 |
| 1924 | 39.3 | 37.5 | 38.3 | 45.5 | 53.2 | 57.7 | 60.8 | 58.2 | 56.4 | 49.8 | 44.4 | 43.6 |
| 1925 | 40.0 | 40.5 | 40.8 | 45.1 | 53.8 | 59.4 | 63.5 | 61.0 | 53.0 | 50.0 | 38.1 | 36.3 |
| 1926 | 39.2 | 43.4 | 43.4 | 48.9 | 50.6 | 56.8 | 62.5 | 62.0 | 57.5 | 46.7 | 41.6 | 39.8 |
| 1927 | 39.4 | 38.5 | 45.3 | 47.1 | 51.7 | 55.0 | 60.4 | 60.5 | 54.7 | 50.3 | 42.3 | 35.2 |
| 1928 | 40.8 | 41.1 | 42.8 | 47.3 | 50.9 | 56.4 | 62.2 | 60.5 | 55.4 | 50.2 | 43.0 | 37.3 |
| 1929 | 34.8 | 31.3 | 41.0 | 43.9 | 53.1 | 56.9 | 62.5 | 60.3 | 59.8 | 49.2 | 42.9 | 41.9 |
Multiple calls to data_color() can provide different color palettes or ranges.
gtcars %>%
dplyr::filter(mfr == "Ferrari", hp < 900) %>%
dplyr::select(model, hp, mpg_c, mpg_h, msrp) %>%
gt() %>%
data_color(
columns = vars(hp),
colors = scales::col_numeric(
palette = c(
"white", "orange", "red"),
domain = c(500, 750))
) %>%
data_color(
columns = vars(mpg_c, mpg_h),
colors = scales::col_numeric(
palette = c(
"white", "green"),
domain = c(10, 25))
) %>%
data_color(
columns = vars(msrp),
colors = scales::col_numeric(
palette = c(
"white", "pink", "red"),
domain = NULL)
)
| model | hp | mpg_c | mpg_h | msrp |
|---|---|---|---|---|
| 458 Speciale | 597 | 13 | 17 | 291744 |
| 458 Spider | 562 | 13 | 17 | 263553 |
| 458 Italia | 562 | 13 | 17 | 233509 |
| 488 GTB | 661 | 15 | 22 | 245400 |
| California | 553 | 16 | 23 | 198973 |
| GTC4Lusso | 680 | 12 | 17 | 298000 |
| FF | 652 | 11 | 16 | 295000 |
| F12Berlinetta | 731 | 11 | 16 | 319995 |
{paleteer} palettesTo make this process easier we can elect to use the {paletteer} package, which makes a wide range of palettes from various R packages readily available.
pizzaplace %>%
dplyr::filter(
type %in% c("chicken", "supreme")) %>%
dplyr::group_by(type, size) %>%
dplyr::summarize(
sold = dplyr::n(),
income = sum(price)
) %>%
gt(rowname_col = "size") %>%
data_color(
columns = vars(sold, income),
colors = scales::col_numeric(
palette = paletteer::paletteer_d(
palette = "ggsci::red_material"
) %>% as.character(),
domain = NULL
)
)
## `summarise()` has grouped output by 'type'. You can override using the `.groups` argument.
| sold | income | |
|---|---|---|
| chicken | ||
| L | 4932 | 102339.0 |
| M | 3894 | 65224.5 |
| S | 2224 | 28356.0 |
| supreme | ||
| L | 4564 | 94258.5 |
| M | 4046 | 66475.0 |
| S | 3377 | 47463.5 |
Factors are typically more appropriate with qualitative palettes, and we can use scales::col_factor() to apply colors to the specific column of interest. Note that the color palette needs to be equal to the unique number of factors. In the example below we pass n = 3 since we have 3 different trim types.
gtcars %>%
dplyr::filter(mfr == "Ferrari", hp < 900) %>%
dplyr::select(model, hp, trim, mpg_h, msrp) %>%
gt() %>%
data_color(
columns = vars(trim),
colors = scales::col_factor(
palette = paletteer::paletteer_d(
n = 3, palette = "colorblindr::OkabeIto"
) %>% as.character(),
domain = NULL
)
)
| model | hp | trim | mpg_h | msrp |
|---|---|---|---|---|
| 458 Speciale | 597 | Base Coupe | 17 | 291744 |
| 458 Spider | 562 | Base | 17 | 263553 |
| 458 Italia | 562 | Base Coupe | 17 | 233509 |
| 488 GTB | 661 | Base Coupe | 22 | 245400 |
| California | 553 | Base Convertible | 23 | 198973 |
| GTC4Lusso | 680 | Base Coupe | 17 | 298000 |
| FF | 652 | Base Coupe | 16 | 295000 |
| F12Berlinetta | 731 | Base Coupe | 16 | 319995 |
Because gt supports HTML, you can optionally “create” HTML strings prior to passing them into gt proper.
color_span <- function(x){paste0("<span style='color: ", x, ";'>", x, "</span>")}
data.frame(
count = c(1L, 2L, 3L, 4L, 5L),
weight_g = c(150.65, 149.65, 171.28, 142.58, 139.04),
color = c("green", "yellow", "yellow", "green", "yellow")
) %>%
mutate(color = color_span(color)) %>%
mutate(color = purrr::map(color, gt::html)) %>%
gt()
| count | weight_g | color |
|---|---|---|
| 1 | 150.65 | green |
| 2 | 149.65 | yellow |
| 3 | 171.28 | yellow |
| 4 | 142.58 | green |
| 5 | 139.04 | yellow |
Combine text into div containers and then “stack” the text on top of each other with alternating color.
stack_function <- function(x){
name <- sub(x = x, pattern = " .*$", replacement = "")
model <- sub(x = x, pattern = ".*? ", replacement = "")
glue::glue(
"<div style='line-height:10px'>
<span style='font-weight:bold;font-variant:small-caps;font-size:14px'>
{name}</div>
<div style='line-height:12px'>
<span style ='font-weight:bold;color:grey;font-size:10px'>
{model}</span></div>"
)
}
head(gtcars) %>%
dplyr::select(mfr, model, year, trim, hp) %>%
gt() %>%
cols_merge(
columns = vars(mfr, model)
) %>%
text_transform(
locations = cells_body(
columns = vars(mfr)
),
fn = stack_function
) %>%
tab_options(
data_row.padding = px(5),
)
| mfr | year | trim | hp |
|---|---|---|---|
Ford
GT |
2017 | Base Coupe | 647 |
Ferrari
458 Speciale |
2015 | Base Coupe | 597 |
Ferrari
458 Spider |
2015 | Base | 562 |
Ferrari
458 Italia |
2014 | Base Coupe | 562 |
Ferrari
488 GTB |
2016 | Base Coupe | 661 |
Ferrari
California |
2015 | Base Convertible | 553 |
We can align text on the first row only even with a suffix (ie symbol at the end). This can be done with just gt, but it’s a bit verbose.
This example applies a percent label to the hp_pct column and properly maintains the decimal place alignment.
head(gtcars) %>%
mutate(hp_pct = (hp/max(hp) * 100)) %>%
dplyr::select(mfr, model, year, trim, hp, hp_pct) %>%
gt() %>%
# use a mono-spaced font
tab_style(
style = cell_text(font = google_font("Fira Mono")),
locations = cells_body(columns = vars(hp_pct))
) %>%
# align the column of interst to right
cols_align(align = "right", columns = vars(hp_pct)) %>%
# round and transform the first row to percent
text_transform(
locations = cells_body(vars(hp_pct), rows = 1),
fn = function(x){
fmt_val <- format(as.double(x), nsmall = 1, digits = 1)
paste0(fmt_val, "%") %>% gt::html()}
) %>%
text_transform(
locations = cells_body(vars(hp_pct), rows = 2:6),
fn = function(x){
# round remaining rows, add a non-breaking space
fmt_val <- format(as.double(x), nsmall = 1, digits = 1)
lapply(fmt_val, function(x) paste0(x, ' ') %>% gt::html())
})
| mfr | model | year | trim | hp | hp_pct |
|---|---|---|---|---|---|
| Ford | GT | 2017 | Base Coupe | 647 | 97.9% |
| Ferrari | 458 Speciale | 2015 | Base Coupe | 597 | 90.3 |
| Ferrari | 458 Spider | 2015 | Base | 562 | 85.0 |
| Ferrari | 458 Italia | 2014 | Base Coupe | 562 | 85.0 |
| Ferrari | 488 GTB | 2016 | Base Coupe | 661 | 100.0 |
| Ferrari | California | 2015 | Base Convertible | 553 | 83.7 |
We can do the same thing with a custom gt function that we’ll call fmt_symbol_first().
fmt_symbol_first <- function(
gt_data,
column = NULL, # column of interest to apply to
symbol = NULL, # symbol to add, optionally
suffix = "", # suffix to add, optionally
decimals = NULL, # number of decimal places to round to
last_row_n, # what's the last row in data?
symbol_first = FALSE # symbol before or after suffix?
) {
# Test and error out if mandatory columns are missing
stopifnot("`symbol_first` argument must be a logical" = is.logical(symbol_first))
stopifnot("`last_row_n` argument must be specified and numeric" = is.numeric(last_row_n))
stopifnot("Input must be a gt table" = class(gt_data)[[1]] == "gt_tbl")
# needs to type convert to double to play nicely with decimals and rounding
# as it's converted to character by gt::text_transform
add_to_first <- function(x, suff = suffix, symb = symbol) {
if (!is.null(decimals)) {
x <- suppressWarnings(as.double(x))
fmt_val <- format(x = x, nsmall = decimals, digits = decimals)
} else {
fmt_val <- x
}
# combine the value, passed suffix, symbol -> html
if (isTRUE(symbol_first)) {
paste0(fmt_val, symb, suff) %>% gt::html()
} else {
paste0(fmt_val, suff, symb) %>% gt::html()
}
}
# repeat non-breaking space for combined length of suffix + symbol
# logic is based on is a NULL passed or not
if (!is.null(symbol) | !identical(as.character(symbol), character(0))) {
suffix <- ifelse(identical(as.character(suffix), character(0)), "", suffix)
length_nbsp <- c(" ", rep(" ", nchar(suffix))) %>%
paste0(collapse = "")
} else {
suffix <- ifelse(identical(as.character(suffix), character(0)), "", suffix)
length_nbsp <- rep(" ", nchar(suffix)) %>%
paste0(collapse = "")
}
# affect rows OTHER than the first row
add_to_remainder <- function(x, length = length_nbsp) {
if (!is.null(decimals)) {
# if decimal not null, convert to double
x <- suppressWarnings(as.double(x))
# then round and format ALL to force specific decimals
fmt_val <- format(x = x, nsmall = decimals, digits = decimals)
} else {
fmt_val <- x
}
paste0(fmt_val, length) %>% lapply(FUN = gt::html)
}
# pass gt object
# align right to make sure the spacing is meaningful
gt_data %>%
cols_align(align = "right", columns = vars({{ column }})) %>%
# convert to mono-font for column of interest
tab_style(
style = cell_text(font = google_font("Fira Mono")),
locations = cells_body(columns = vars({{ column }}))
) %>%
# transform first rows
text_transform(
locations = cells_body(vars({{ column }}), rows = 1),
fn = add_to_first
) %>%
# transform remaining rows
text_transform(
locations = cells_body(vars({{ column }}), rows = 2:last_row_n),
fn = add_to_remainder
)
}
We can then use the function as a one-liner, and format just that column of interest.
head(gtcars) %>%
mutate(hp_pct = (hp/max(hp) * 100)) %>%
dplyr::select(mfr, model, year, trim, hp, hp_pct) %>%
gt() %>%
opt_table_lines() %>%
fmt_symbol_first(column = hp_pct, decimals = 1, suffix = "%", last_row_n = 6)
| mfr | model | year | trim | hp | hp_pct |
|---|---|---|---|---|---|
| Ford | GT | 2017 | Base Coupe | 647 | 97.9% |
| Ferrari | 458 Speciale | 2015 | Base Coupe | 597 | 90.3 |
| Ferrari | 458 Spider | 2015 | Base | 562 | 85.0 |
| Ferrari | 458 Italia | 2014 | Base Coupe | 562 | 85.0 |
| Ferrari | 488 GTB | 2016 | Base Coupe | 661 | 100.0 |
| Ferrari | California | 2015 | Base Convertible | 553 | 83.7 |
We can embed sparkline plots with some help from the kableExtra package.
mtcars %>%
group_by(cyl) %>%
summarize(mpg_data = list(mpg), .groups = "drop") %>%
gt() %>%
text_transform(
locations = cells_body(columns = vars(mpg_data)),
fn = function(x) {
data_in <- purrr::pluck(., "_data", "mpg_data")
plot <- purrr::map(
data_in, ~ kableExtra::spec_plot(
.x, ylim = range(mtcars$mpg),
same_lim = TRUE, width = 300, height = 70
)
)
plot <- purrr::map_chr(plot, "svg_text")
}
)
| cyl | mpg_data |
|---|---|
| 4 | |
| 6 | |
| 8 |
We can alternatively write a function to do something similar.
gt_plot <- function(table_data, plot_col, data_col, plot_fun, ...){
# save the data extract ahead of time
# to be used in our anonymous function below
data_in = pluck(table_data, "_data", data_col)
text_transform(
table_data,
# note the use of {{}} here - this is tidy eval
# that allows you to indicate specific columns
locations = cells_body(columns = vars({{plot_col}})),
fn = function(x){
plot <- purrr::map(data_in, plot_fun, width = 300, height = 70, same_lim = FALSE, ...)
plot_svg <- purrr::map(plot, "svg_text")
purrr::map(plot_svg, gt::html)
}
)
}
And then we can use that function!
mtcars %>%
group_by(cyl) %>%
summarize(mpg_data = list(mpg), .groups = "drop") %>%
gt() %>%
# note you can leave mpg_data unquoted for the tidyeval
# but have to quote mpg_data for the pluck
gt_plot(mpg_data, "mpg_data", plot_fun = kableExtra::spec_plot)
| cyl | mpg_data |
|---|---|
| 4 | |
| 6 | |
| 8 |
We can use the sparkline package to embed interactive sparklines.
gt_spark <- function(table_data, plot_col, data_col){
# save the data extract ahead of time
# to be used in our anonymous function below
data_in = purrr::pluck(table_data, "_data", data_col)
text_transform(
table_data,
# note the use of {{}} here - this is tidy eval
# that allows you to indicate specific columns
locations = cells_body(columns = vars({{plot_col}})),
fn = function(x){
sparkline_plot <- purrr::map(
data_in,
~sparkline::spk_chr(values = .x, chartRangeMin = 0)
)
purrr::map(sparkline_plot, gt::html)
}
)
}
We can then apply the function to work very succinctly, referencing only the internal list-column data.
mtcars %>%
group_by(cyl) %>%
summarize(mpg_data = list(mpg), .groups = "drop") %>%
gt() %>%
# note you can leave mpg_data unquoted for the tidyeval
# but have to quote mpg_data for the pluck
gt_spark(mpg_data, "mpg_data")
| cyl | mpg_data |
|---|---|
| 4 | |
| 6 | |
| 8 |
Tooltips can be added with HTML tags.
library(htmltools)
#
# Add tooltip to column labels
with_tooltip <- function(value, tooltip) {
tags$abbr(
style = "text-decoration: underline;
text-decoration-style: solid; color: blue",
title = tooltip,
value
) %>%
as.character()
}
mtcars %>%
head() %>%
tibble::rownames_to_column() %>%
select(rowname, mpg:hp) %>%
gt() %>%
cols_label(
mpg = gt::html(with_tooltip("MPG", "Miles per Gallon")),
cyl = gt::html(with_tooltip("CYL", "Number of Cylinders")),
disp = gt::html(with_tooltip("DISP", "Displacement")),
hp = gt::html(with_tooltip("HP", "Horsepower")),
)
| MPG | CYL | DISP | HP | |
|---|---|---|---|---|
| Mazda RX4 | 21.0 | 6 | 160 | 110 |
| Mazda RX4 Wag | 21.0 | 6 | 160 | 110 |
| Datsun 710 | 22.8 | 4 | 108 | 93 |
| Hornet 4 Drive | 21.4 | 6 | 258 | 110 |
| Hornet Sportabout | 18.7 | 8 | 360 | 175 |
| Valiant | 18.1 | 6 | 225 | 105 |
You can add arbitrary icons with the fontawesome R package.
mtcars %>%
head() %>%
gt() %>%
text_transform(
locations = cells_body(columns = vars(cyl), rows = cyl == 4),
fn = function(x){gt::html(fontawesome::fa("truck-pickup", fill = "blue"))}
) %>%
text_transform(
locations = cells_body(columns = vars(cyl), rows = cyl == 6),
fn = function(x){gt::html(fontawesome::fa("truck", fill = "grey"))}
) %>%
text_transform(
locations = cells_body(columns = vars(cyl), rows = cyl == 8),
fn = function(x){gt::html(fontawesome::fa("truck-monster", fill = "red"))}
)
| mpg | cyl | disp | hp | drat | wt | qsec | vs | am | gear | carb |
|---|---|---|---|---|---|---|---|---|---|---|
| 21.0 | 160 | 110 | 3.90 | 2.620 | 16.46 | 0 | 1 | 4 | 4 | |
| 21.0 | 160 | 110 | 3.90 | 2.875 | 17.02 | 0 | 1 | 4 | 4 | |
| 22.8 | 108 | 93 | 3.85 | 2.320 | 18.61 | 1 | 1 | 4 | 1 | |
| 21.4 | 258 | 110 | 3.08 | 3.215 | 19.44 | 1 | 0 | 3 | 1 | |
| 18.7 | 360 | 175 | 3.15 | 3.440 | 17.02 | 0 | 0 | 3 | 2 | |
| 18.1 | 225 | 105 | 2.76 | 3.460 | 20.22 | 1 | 0 | 3 | 1 |
You can take the icons example a step further, and assign rating stars. For this example, we’re creating HTML content in the data itself, before passing it into gt. This example adapted from reactable.
# note you could use ANY font-awesome logo
# https://fontawesome.com/cheatsheet
rating_stars <- function(rating, max_rating = 5) {
rounded_rating <- floor(rating + 0.5) # always round up
stars <- lapply(seq_len(max_rating), function(i) {
if (i <= rounded_rating) fontawesome::fa("star", fill= "orange") else fontawesome::fa("star", fill= "grey")
})
label <- sprintf("%s out of %s", rating, max_rating)
div_out <- div(title = label, "aria-label" = label, role = "img", stars)
as.character(div_out) %>%
gt::html()
}
mtcars %>%
head() %>%
mutate(rating = purrr::map(sample(1:5, size = 6, TRUE), rating_stars)) %>%
gt()
| mpg | cyl | disp | hp | drat | wt | qsec | vs | am | gear | carb | rating |
|---|---|---|---|---|---|---|---|---|---|---|---|
| 21.0 | 6 | 160 | 110 | 3.90 | 2.620 | 16.46 | 0 | 1 | 4 | 4 | |
| 21.0 | 6 | 160 | 110 | 3.90 | 2.875 | 17.02 | 0 | 1 | 4 | 4 | |
| 22.8 | 4 | 108 | 93 | 3.85 | 2.320 | 18.61 | 1 | 1 | 4 | 1 | |
| 21.4 | 6 | 258 | 110 | 3.08 | 3.215 | 19.44 | 1 | 0 | 3 | 1 | |
| 18.7 | 8 | 360 | 175 | 3.15 | 3.440 | 17.02 | 0 | 0 | 3 | 2 | |
| 18.1 | 6 | 225 | 105 | 2.76 | 3.460 | 20.22 | 1 | 0 | 3 | 1 |
You can embed expandable sections with <details> HTML, and we can build up some contents of the details tag with the use of htmltools.
library(htmltools)
source_details <- paste0(
"<details>", "<summary><strong>Table Key, click to expand</strong></summary>",
div("cyl: Cylinders"), div("disp: Displacement"), div("hp: Horsepower"),
"</details"
)
head(mtcars) %>%
gt() %>%
tab_source_note(source_note = html(source_details))
| mpg | cyl | disp | hp | drat | wt | qsec | vs | am | gear | carb |
|---|---|---|---|---|---|---|---|---|---|---|
| 21.0 | 6 | 160 | 110 | 3.90 | 2.620 | 16.46 | 0 | 1 | 4 | 4 |
| 21.0 | 6 | 160 | 110 | 3.90 | 2.875 | 17.02 | 0 | 1 | 4 | 4 |
| 22.8 | 4 | 108 | 93 | 3.85 | 2.320 | 18.61 | 1 | 1 | 4 | 1 |
| 21.4 | 6 | 258 | 110 | 3.08 | 3.215 | 19.44 | 1 | 0 | 3 | 1 |
| 18.7 | 8 | 360 | 175 | 3.15 | 3.440 | 17.02 | 0 | 0 | 3 | 2 |
| 18.1 | 6 | 225 | 105 | 2.76 | 3.460 | 20.22 | 1 | 0 | 3 | 1 |
Table Key, click to expandcyl: Cylinders disp: Displacement hp: Horsepower | ||||||||||
There are different ways to create bar charts, but the example below is adapted from the reactable documentation. The original source on CSS bars using HTML and CSS.
bar_chart <- function(label, height = "16px", fill = "#00bfc4", background = "white") {
bar <- glue::glue(
"<div style='background:{fill};width:{label}%;height:{height};'></div>"
)
chart <- glue::glue(
"<div style='flex-grow:1;margin-left:8px;background:{background};'>{bar}</div>"
)
glue::glue(
"<div style='display:flex;align-items:left';>{chart}</div>"
) %>%
gt::html()
}
mtcars %>%
head() %>%
mutate(
mpg_val = mpg/max(mpg) * 100,
mpg_plot = purrr::map(mpg_val, ~bar_chart(label = .x)),
mpg_plot2 = purrr::map(
mpg_val,
~bar_chart(label = .x, fill = "#fc5185", background = "#e1e1e1")
),
) %>%
select(cyl, hp, disp, mpg, mpg_plot, mpg_plot2) %>%
gt() %>%
cols_align(align = "left", columns = vars(mpg_plot))
| cyl | hp | disp | mpg | mpg_plot | mpg_plot2 |
|---|---|---|---|---|---|
| 6 | 110 | 160 | 21.0 | ||
| 6 | 110 | 160 | 21.0 | ||
| 4 | 93 | 108 | 22.8 | ||
| 6 | 110 | 258 | 21.4 | ||
| 8 | 175 | 360 | 18.7 | ||
| 6 | 105 | 225 | 18.1 |
The function provides a convenient way to generate an HTML fragment with an image URL. Because this function is currently HTML-based, it is only useful for HTML table output. To use this function inside of data cells, it is recommended that the text_transform() function is used.
r_png_url <- "https://www.r-project.org/logo/Rlogo.png"
dplyr::tibble(
pixels = px(seq(10, 35, 5)),
image = seq(10, 35, 5)
) %>%
gt() %>%
text_transform(
locations = cells_body(vars(image)),
fn = function(x) {
web_image(
url = r_png_url,
height = as.numeric(x)
)
}
)
| pixels | image |
|---|---|
| 10px | |
| 15px | |
| 20px | |
| 25px | |
| 30px | |
| 35px |
You can include multiple images by parsing the url along with purrr::map() or lapply()
tibble::tribble(
~team_abb, ~headshot_href, ~short_name, ~qbr_total, ~qb_plays,
"GB", "https://a.espncdn.com/i/headshots/nfl/players/full/8439.png", "A. Rodgers", 84.4, 608,
"KC", "https://a.espncdn.com/i/headshots/nfl/players/full/3139477.png", "P. Mahomes", 82.9, 710,
"BUF", "https://a.espncdn.com/i/headshots/nfl/players/full/3918298.png", "J. Allen", 81.7, 729,
"TEN", "https://a.espncdn.com/i/headshots/nfl/players/full/14876.png", "R. Tannehill", 78.3, 594,
"MIA", "https://a.espncdn.com/i/headshots/nfl/players/full/8664.png", "R. Fitzpatrick", 76.9, 324,
"NO", "https://a.espncdn.com/i/headshots/nfl/players/full/2580.png", "D. Brees", 74.6, 428,
"BAL", "https://a.espncdn.com/i/headshots/nfl/players/full/3916387.png", "L. Jackson", 73.7, 585,
"SEA", "https://a.espncdn.com/i/headshots/nfl/players/full/14881.png", "R. Wilson", 73.5, 716,
"TB", "https://a.espncdn.com/i/headshots/nfl/players/full/2330.png", "T. Brady", 72.5, 681,
"CLE", "https://a.espncdn.com/i/headshots/nfl/players/full/3052587.png", "B. Mayfield", 72.2, 597
) %>%
gt() %>%
text_transform(
locations = cells_body(vars(headshot_href)),
fn = function(x) {purrr::map(x,~ web_image(url = .x, height = 30))}
)
| team_abb | headshot_href | short_name | qbr_total | qb_plays |
|---|---|---|---|---|
| GB | A. Rodgers | 84.4 | 608 | |
| KC | P. Mahomes | 82.9 | 710 | |
| BUF | J. Allen | 81.7 | 729 | |
| TEN | R. Tannehill | 78.3 | 594 | |
| MIA | R. Fitzpatrick | 76.9 | 324 | |
| NO | D. Brees | 74.6 | 428 | |
| BAL | L. Jackson | 73.7 | 585 | |
| SEA | R. Wilson | 73.5 | 716 | |
| TB | T. Brady | 72.5 | 681 | |
| CLE | B. Mayfield | 72.2 | 597 |